捣蛋phpwind之WindFrameWork
一直都有关注phpwind这个开源产品,从9.0开始就好关注拉,因为官方说把之前的代码重写了一遍,融入了windFramework这个框架,代码真的挺优美的,今日在做社区的一些功能,心血来潮就参考了phpwind的代码,确实学到了不少
其实外界一直说这个框架模范了yii,但我觉得这个框架一定是有java功底的人写的,因为真的有很多java的风格和影子,不信?请看下文
启动xdebug ,看下执行流程如何
windClassProxy 是个什么东东,我记得之前学struct2的时候也有类似的这个玩意 ,跟进去看看就知道啦
1 <?php 2 /** 3 * 类代理定义 4 * 5 * 通过使用类代理机制,可以实现对类方法或属性的监听过滤机制.<code> 6 * //相关组件配置,只需设置 proxy为true,就可以通过组件工厂创建一个具有代理功能的类实例对象. 7 * <component name='windApplication' path='WIND:web.WindWebApplication' 8 * scope='singleton' proxy='true'> 9 * <properties> 10 * <property name='dispatcher' ref='dispatcher' /> 11 * <property name='handlerAdapter' ref='router' /> 12 * </properties> 13 * </component> 14 * $object = Wind::getComponents('windApplication'); 15 * $object->registerEventListener('runProcess', new Listener()); 16 * </code> 17 * @author Qiong Wu <papa0924@gmail.com> 18 * @copyright ©2003-2103 phpwind.com 19 * @license http://www.windframework.com 20 * @version $Id: WindClassProxy.php 3681 2012-06-18 02:45:28Z yishuo $ 21 * @package base 22 */ 23 class WindClassProxy { 24 /** 25 * 默认过滤链类型定义 26 * 27 * @var string 28 */ 29 protected $_class_interceptorChain = 'WIND:filter.WindHandlerInterceptorChain'; 30 31 /** 32 * 过滤链对象 33 * 34 * @var WindHandlerInterceptorChain 35 */ 36 private $_interceptorChain = null; 37 protected $_className = ''; 38 protected $_classPath = ''; 39 protected $_instance = null; 40 protected $_listener = array(); 41 42 /** 43 * @param object $targetObj 需要被代理监听的类对象实例 默认为null 44 */ 45 public function __construct($targetObject = null) { 46 $targetObject && $this->registerTargetObject($targetObject); 47 } 48 49 /** 50 * 注册事件以及事件监听类 51 * 52 * 通过调用该方法,将事件以及对事件的监听方法注册进来,当事件方法被调用的时候监听的方法被触发.例:<code> 53 * <component name='windApplication' path='WIND:web.WindWebApplication' 54 * scope='singleton' proxy='true'>...</component> 55 * $object = Wind::getComponents('windApplication'); 56 * $object->registerEventListener('runProcess', new Listener()); 57 * </code> 58 * @param object $listener 事件监听器 59 * @param stinrg $event 被监听的事件 60 * @return void 61 */ 62 public function registerEventListener($listener, $event) { 63 $this->_listener[$event][] = $listener; 64 } 65 66 /** 67 * 注册目标对象,如果已经注册了不重复注册 68 * 69 * WindFactory中创建类代理的一段例子:<code> 70 * $instance = new Object(); 71 * $this->addClassDefinitions($alias, array('path' => $proxy, 'scope' => 'prototype')); 72 * $proxy = $this->getInstance($alias); 73 * $proxy->registerTargetObject($instance); 74 * $instance->_proxy = $proxy; 75 * </code><note><b>注意:</b>$instance继承自WindModule</note> 76 * @param object $targetObject 77 * @return WindClassProxy 78 */ 79 public function registerTargetObject($targetObject) { 80 $this->_className = get_class($targetObject); 81 $this->_instance = $targetObject; 82 return $this; 83 } 84 85 /** 86 * 监听类方法 87 * 88 * @param string $methodName 方法名 89 * @param array $args 方法参数 90 * @return mixed 91 * @throws WindException 92 */ 93 public function __call($methodName, $args) { 94 $listeners = isset($this->_listener[$methodName]) ? $this->_listener[$methodName] : array(); 95 if (empty($listeners)) return call_user_func_array(array($this->_instance, $methodName), $args); 96 $interceptorChain = $this->_getInterceptorChain($methodName); 97 $interceptorChain->addInterceptors($listeners); 98 $interceptorChain->setCallBack(array($this->_getInstance(), $methodName), $args); 99 return call_user_func_array(array($interceptorChain->getHandler(), 'handle'), (array) $args); 100 } 101 102 /** 103 * 创建并返回过滤链,如果过滤链已经被创建不重复创建 104 * 105 * @param string $event 事件名称 默认值为空 106 * @return WindHandlerInterceptorChain 107 * @throws WindException 108 */ 109 private function _getInterceptorChain($event = '') { 110 if (null === $this->_interceptorChain) { 111 $chain = Wind::import($this->_class_interceptorChain); 112 $this->_interceptorChain = WindFactory::createInstance($chain); 113 } 114 $this->_interceptorChain->reset(); 115 return $this->_interceptorChain; 116 } 117 118 /** 119 * 返回当前代理对象的真实类对象 120 * 121 * @return object 122 */ 123 public function _getInstance() { 124 return $this->_instance; 125 } 126 127 /** 128 * 返回当前代理对象的真实类名称 129 * 130 * @return string 131 */ 132 public function _getClassName() { 133 return $this->_className; 134 } 135 136 /** 137 * 返回当前代理对象的真实类的路径信息 138 * 139 * @return string 140 */ 141 public function _getClassPath() { 142 return $this->_classPath; 143 } 144 145 /** 146 * 设置类名称 147 * 148 * @param string $className 149 * @return void 150 */ 151 public function _setClassName($className) { 152 $this->_className = $className; 153 } 154 155 /** 156 * 设置类路径 157 * 158 * @param string $classPath 159 * @return void 160 */ 161 public function _setClassPath($classPath) { 162 $this->_setClassName(Wind::import($classPath)); 163 $this->_classPath = $classPath; 164 } 165 } 166 ?>
一个代理类,那么该怎么用呢,先看看下面把
1 /** 2 * 解析action过滤链的配置信息 3 * 4 * @param WindSimpleController $handler 5 * @return void 6 */ 7 protected function resolveActionFilters(&$handler) { 8 if (!$filters = $this->getConfig('filters')) return; 9 /* @var $cache AbstractWindCache */ 10 $_filters = array(); 11 if ($cache = Wind::getComponent('windCache')) { 12 $_filters = $cache->get('filters'); 13 } 14 $_token = $this->handlerAdapter->getModule() . '/' . $this->handlerAdapter->getController() . '/' . $this->handlerAdapter->getAction(); 15 if (!isset($_filters[$_token])) { 16 foreach ($filters as $_filter) { 17 if (empty($_filter['class'])) continue; 18 $_pattern = empty($_filter['pattern']) ? '' : $_filter['pattern']; 19 unset($_filter['pattern']); 20 if ($_pattern) { 21 $_pattern = str_replace(array('*', '/'), array('\w*', '\/'), $_pattern); 22 if (in_array($_pattern[0], array('~', '!'))) { 23 $_pattern = substr($_pattern, 1); 24 if (preg_match('/^' . $_pattern . '$/i', $_token)) continue; 25 } else { 26 if (!preg_match('/^' . $_pattern . '$/i', $_token)) continue; 27 } 28 } 29 $_filters[$_token][] = $_filter; 30 } 31 $cache && $cache->set('filters', $_filters); 32 } 33 if (empty($_filters[$_token])) return; 34 /* @var $proxy WindClassProxy */ 35 $proxy = WindFactory::createInstance(Wind::import('WIND:filter.proxy.WindClassProxy')); 36 $proxy->registerTargetObject($handler); 37 foreach ($_filters[$_token] as $value) { 38 $proxy->registerEventListener( 39 $this->factory->createInstance(Wind::import($value['class']), 40 array($handler->getForward(), $handler->getErrorMessage(), $this->handlerAdapter, $value)), 41 'doAction'); 42 } 43 $handler = $proxy; 44 }
一系列的 filter,跟java好像把是不是 ,看下java是怎样定义fliter的吧
1 import java.io.*; 2 import javax.servlet.*; 3 import javax.servlet.http.*; 4 import java.util.*; 5 6 // 实现 Filter 类 7 public class LogFilter implements Filter { 8 public void init(FilterConfig config) 9 throws ServletException{ 10 // 获取初始化参数 11 String testParam = config.getInitParameter("test-param"); 12 13 // 输出初始化参数 14 System.out.println("Test Param: " + testParam); 15 } 16 public void doFilter(ServletRequest request, 17 ServletResponse response, 18 FilterChain chain) 19 throws java.io.IOException, ServletException { 20 21 // 获取客户机的 IP 地址 22 String ipAddress = request.getRemoteAddr(); 23 24 // 记录 IP 地址和当前时间戳 25 System.out.println("IP "+ ipAddress + ", Time " 26 + new Date().toString()); 27 28 // 把请求传回过滤链 29 chain.doFilter(request,response); 30 } 31 public void destroy( ){ 32 /* 在 Filter 实例被 Web 容器从服务移除之前调用 */ 33 } 34 }
Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping)
1 <filter> 2 <filter-name>LogFilter</filter-name> 3 <filter-class>LogFilter</filter-class> 4 <init-param> 5 <param-name>test-param</param-name> 6 <param-value>Initialization Paramter</param-value> 7 </init-param> 8 </filter> 9 <filter-mapping> 10 <filter-name>LogFilter</filter-name> 11 <url-pattern>/*</url-pattern> 12 </filter-mapping>
是不是好像阿,看下这段代码
1 $_token = $this->handlerAdapter->getModule() . '/' . $this->handlerAdapter->getController() . '/' . $this->handlerAdapter->getAction(); 2 if (!isset($_filters[$_token])) { 3 foreach ($filters as $_filter) { 4 if (empty($_filter['class'])) continue; 5 $_pattern = empty($_filter['pattern']) ? '' : $_filter['pattern']; 6 unset($_filter['pattern']); 7 if ($_pattern) { 8 $_pattern = str_replace(array('*', '/'), array('\w*', '\/'), $_pattern); 9 if (in_array($_pattern[0], array('~', '!'))) { 10 $_pattern = substr($_pattern, 1); 11 if (preg_match('/^' . $_pattern . '$/i', $_token)) continue; 12 } else { 13 if (!preg_match('/^' . $_pattern . '$/i', $_token)) continue; 14 } 15 } 16 $_filters[$_token][] = $_filter; 17 } 18 $cache && $cache->set('filters', $_filters); 19 }
如果url匹配的话,该过滤器就加入执行队列里面
看看phpwind的过滤器是怎样书写的
<?php Wind::import('WIND:filter.WindActionFilter'); /** * * CSRF安全处理filter * * @author liusanbian <liusanbian@aliyun.com> * @copyright ©2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id$ */ class PwCsrfTokenFilter extends WindActionFilter { /* (non-PHPdoc) * @see WindHandlerInterceptor::preHandle() */ public function preHandle() { if (true !== $this->getRequest()->isPost() || empty($_POST)) return ; /* @var $windToken IWindSecurityToken */ $windToken = Wind::getComponent('windToken'); $csrf_token = $this->getInput('csrf_token', 'POST'); if (true !== $windToken->validateToken($csrf_token, 'csrf_token')) { $this->errorMessage->sendError('Sorry, CSRF verification failed(token missing or incorrect),refresh to try again.'); } } /* (non-PHPdoc) * @see WindHandlerInterceptor::postHandle() */ public function postHandle() {} } ?>
简直就是异曲同工阿 ,再看看它的父类
<?php Wind::import('WIND:fitler.WindHandlerInterceptor'); /** * action拦截器父类 * 继承实现拦截链preHandle(前置)和postHandle(后置)职责.将实现的拦截链添加到应用配置中,使之生效: * 例如实现MyFilter,则需要在应用配置中添加如下配置: * <code> * 'filters' => array( * 'class' => 'WIND:filter.WindFilterChain', //设置使用的拦截链实现 * 'filter1' => array( * 'class' => * 'MYAPP:filter.MyFilter', //设置设置实现的MyFilter类路径,MYAPP必须是一个有效的经过注册的命名空间 * 'pattern' => '*', //此处设置该拦截规则应用的范围,*意味着所有的action都将会应用该拦截规则 * ) * ) * </code> * 关于pattern的设置说明如下: * <ul> * <li>*:则所有的请求都将会应用该拦截器</li> * <li>moduleA*: 则所有配置的moduleA模块下的请求都将会应用该拦截器</li> * <li>moduleA_index*: 则moduleA模块下的indexController下的所有Action请求都将会应用该拦截器</li> * <li>moduleA_index_add*: 则module模块下的indexController下的addAction将会应用该拦截器</li> * </ul> * 用户可以在filter中添加自己的特殊配置:比如: * <code> * 'filters' => array( * 'class' => 'WIND:filter.WindFilterChain', * 'filter1' => array( * 'class' => 'MYAPP:filter.TestFilter', * 'pattern' => '*', * 'isOpen' => '1', //添加的配置 * ) * ) * </code> * 则在自己的TestFilter中设置一个属性名为isOpen同时设置该属性为protected权限,那么在使用的时候该配置的值将会赋值给该属性. * * @author Qiong Wu <papa0924@gmail.com> * @copyright ©2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id: WindActionFilter.php 3829 2012-11-19 11:13:22Z yishuo $ * @package filter */ abstract class WindActionFilter extends WindHandlerInterceptor { /** * action跳转类 * * @var WindForward */ protected $forward = null; /** * 错误处理类 * * @var WindErrorMessage */ protected $errorMessage = null; /** * 路由对象 * * @var AbstractWindRouter */ protected $router = null; /** * 构造函数 * 初始化类属性 * * @param WindForward $forward * 设置当前的forward对象 * @param WindErrorMessage $errorMessage * 设置错误处理的errorMessage * @param AbstractWindRouter $router * 路由对象 * @param array $args * 接受数组传递,数组以关联数组的方式给出,如果存在属性和关联数组中的key相同则将该key对应值设置给该属性. */ public function __construct($forward, $errorMessage, $router, $args = array()) { $this->forward = $forward; $this->errorMessage = $errorMessage; $this->router = $router; foreach ($args as $key => $value) property_exists($this, $key) && $this->$key = $value; } /** * 设置模板数据 * 此方法设置的参数,作用域仅仅只是在当前模板中可用,调用的方法为{$varName} * * @param string|array|object $data * 需要设置输出的参数 * @param string $key * 参数的名字,默认为空,如果key为空,并且$data是数组或是对象的时候,则$data中的元素将会作为单独的参数保存到输出数据中. */ protected function setOutput($data, $key = '') { $this->forward->setVars($data, $key); } /** * 从指定源中根据输入的参数名获得输入数据 * * @param string $name * 需要获取的值的key * @param string $type * 获取数据源,可以是(GET POST COOKIE)中的一个,每种都将从各自的源中去获取对应的数值: * <ul> * <li>GET: 将从$_GET中去获取数据</li> * <li>POST: 将从$_POST中去获取数据</li> * <li>COOKIE: 将从$_COOKIE中去获取数据</li> * <li>其他值: * 将依次从request对象的attribute,$_GET,$_POST,$_COOKIE,$_REQUEST,$_ENV,$_SERVER中去尝试获取该值.</li> * </ul> * 该参数默认为空 * @return array string <ul> * <li>第一个元素: 获得的用户输入的值</li> * <li>第二个元素:执行$callback之后返回的值</li> * </ul> */ protected function getInput($name, $type = '') { $value = ''; switch (strtolower($type)) { case 'get': $value = $this->getRequest()->getGet($name); break; case 'post': $value = $this->getRequest()->getPost($name); break; case 'cookie': $value = $this->getRequest()->getCookie($name); break; default: $value = $this->getRequest()->getRequest($name); } return $value; } } ?>
下面这个类才是关键
<?php /** * 拦截器基类 * * 该类是拦截器机制的核心实现,提供接口: * <ul> * <li>{@link preHandle()}: 抽象接口,前置操作,需要子类实现</li> * <li>{@link postHandle()}: 抽象接口,后置操作,需要子类实现</li> * <li>{@link handle()}: 入口接口,调用拦截器的实现.</li> * </ul> * 该拦截器需要配合拦截链WindHandlerInterceptorChain实现真正的拦截链. * * the last known user to change this file in the repository <$LastChangedBy: yishuo $> * @author Qiong Wu <papa0924@gmail.com> * @copyright ©2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id: WindHandlerInterceptor.php 3113 2011-11-11 07:28:09Z yishuo $ * @package filter */ abstract class WindHandlerInterceptor extends WindModule { /** * 保存执行的结果 * * @var mixed */ protected $result = null; /** * 保存拦截链 * * 用以传递控制到下一个拦截器 * * @var WindHandlerInterceptorChain */ protected $interceptorChain = null; /** * 拦截器的前置操作 * * @param mixed $var=.. 参数列表将会从handle接口中传递继承 * @return null|mixed 如果返回为null则将会继续执行下一个拦截器,如果返回不为null则会中断拦截链的执行 */ abstract public function preHandle(); /** * 拦截器的后置操作 * * @param mixed $var=.. 参数列表将会从handle接口中传递继承 */ abstract public function postHandle(); /** * 拦截器的执行入口 * * @param mixed $var=.. 该接口接受任意参数,并将依次传递给拦截器的前置和后置操作 * @return mixed 返回拦截链执行的最终结果 */ public function handle() { $args = func_get_args(); $this->result = call_user_func_array(array($this, 'preHandle'), $args); if ($this->result !== null) { return $this->result; } if (null !== ($handler = $this->interceptorChain->getHandler())) { $this->result = call_user_func_array(array($handler, 'handle'), $args); } else { $this->result = call_user_func_array(array($this->interceptorChain, 'handle'), $args); } call_user_func_array(array($this, 'postHandle'), $args); return $this->result; } /** * 设置拦截链对象 * * @param WindHandlerInterceptorChain $interceptorChain */ public function setHandlerInterceptorChain($interceptorChain) { $this->interceptorChain = $interceptorChain; } } ?>
preHandle 和 postHandle 这两个方法是抽象的,需要子类实现的,handle这个东西可以在子类调用阿
而且 setHandlerInterceptorChain 这个可以换不同的拦截器链,比较灵活把
今日就分享到这里吧,下节再继续
---恢复内容结束---