php 事件驱动编程:(http://hi.baidu.com/yiqing95)
*
事件驱动在桌面型应用中是很是广泛的,好比你点击鼠标,点击某个按钮应用程序就得对你的动做作出相应的反应,从程序员的角度看,有两个角色须要识别:一个是用户 你个就是你构建的系统,此外无它! 如今用中间者的身份看两者的行为模式:用户老是在向系统发送某种信息,这种信息促使系统进行响应,全部的请求从更抽象的角度观察,无外乎两种:命令与请求;请严格区分这二者,命令是促使系统作某种变动(好比删除一条记录),而请求是须要系统返回某些信息(好比查询);固然命令也通常会反馈一些信息告诉系统执行状况(是成功了仍是失败了,仍是发生了不可预知的异常。);从这里能够获得一个适应全部状况的编程接口
execute(IN paramInformation):returnedInformation; //参数和返回值有时可选
事件模型是对 “变动——响应”机制的实现,一种机制老是可用多个方法来实现,这取决于编程者如何想问题的。php内部并无所谓的事件,事件分发器的概念,但并不表明咱们不能用事件模型来处理php世界里的问题!!
*
**
理解事件:
什么是事件,从你易接受的角度看就是键盘,鼠标等人物动做引发的变动,这个变动就叫事件:鼠标单击事件,鼠标拖动事件,键盘点击事件...... 。 从咱们人类对世界的认识角度来看:全部对你,或对你们有意义的变动就是事件,好比918事件,1212事件911事件,天然界无时不刻发生着某种变化,而只有这些变化引发你足够重视时他才是事件,好比你能够说1980年发生了一个重大事件---那一天我诞生了(这对你父母来讲固然是重大事件,至少比911重大些);因此最后阐明一点:事件就是变化,任何变化的结果都能被定义为事件,因此什么时事件有你来决定。
从计算机角度看,全部的01序列的位变化均可定义为事件,仍取决于你,但计算机最底层的实现无外乎读与写,因此读和写性质的操做结果(或者这个发生)就能够被称为事件。还有一个比较特殊的变化是时间,从四维空间来看虽然三维空间上的某些东西没有变,但时间维老是在向前推动,全部时间也能够引发事件。
任何变动或条件的知足都能被定义为事件,若是不产生信息交换,一个封闭的系统对外界不会产生有意义的事件,你若是只设计了一个没有键盘和鼠标的电脑,估计电脑充其量也就是一个看着复杂的电视或DVD而已。为此程序都向外部暴露了某些接口,经过这些接口咱们就可以触发某些变动。这些接口能够是GUI上的可视化接口,也能够是命令行方式的接口,固然从仿形递归角度看,更小一些的组件(子系统,类..)会向外界暴露一些API。
外界暴露的接口是通向系统内部的引脚,也是触发系统内部事件的导火索,在web开发中这些接口,如你所见好比超连接,按钮,输入框,下拉菜单等,这些东西都能引起系统对其响应。从上面的事件理解来看,事件是能够被忽略的(好比911对别人来讲是事件对我就不是,又不会影响我什么),并且要紧的一点是事件能够传播!!变化是能够致使变化的,因此一个事件就有可能致使另外一个事件。这些都是事件的特征。值得一提的事想一想若是造成环行事件传播路径会怎样??
**
***
咱们是系统的设计者,从系统角度来看咱们须要响应用户对系统的请求与命令。也就是须要响应一些事件,而后作某些处理,把最终的执行状况或请求的信息返回给客户。在系统的边界处咱们要把这些事件跟系统内部预约义好的响应模块映射起来:
switch($_GET[‘action’]) {
case “edit_record”:
edit_record();
break;
case “view_record”:
view_record();
break;
}
以上代码是典型的如何处理用户经过HTTP GET方法发送过来的编辑与查询请求的。这里其实咱们就在响应用户的超连接或者按钮事件,(或者可能js触发的ajax调用)。
注意到这些系统边界的处理老是有着极大的重复性,并且当系统不断壮大时咱们要不停的增长这些映射是多么繁琐的事情,并且咱们在违反DRY(don't repeat yourself 不要重复你本身);
在OOP领域对事件驱动有着至关成熟的模式能够用,设计模式的宗旨之一就是不要重复发明轮子,因此能够直接拿来用了。在上面函数式的事件处理轮廓中有两个角色,一个就是事件分发器(由swith语句充当),另外一个就是事件响应器(由那两个函数来承当)再有一个就是case后面的东西 那个就是事件!!!。
看这个URl:http://myserver/interface.php?event=edit 这个URL就明显的代表用户的意图,要系统对edit事件进行响应,实际上如今好多开源项目的url设计都有不少这种思路,好比act ,ac前缀表明的就是命令/事件 后台会找对应的处理脚本,你在康盛公司的项目中仔细观察URL的规律就可看出某些东西。因此设计其实能够从URL开始的,URL就是接口,这个在面向对象中可认为是面向接口编程:举个例子,你想编辑某个书籍的信息,你就能够先设计URL,如:myserver/book.php?act=edit&bid=334455 ,想查看某个书籍的信息就能够 myserver/book.php?act=detail&bid=334455。 等依次类推,这也可认为是URL驱动的设计。
事件分发器的职责是来根据事件查询事件响应器的,并把处理流程传递给事件响应器,他自己并不作事件处理工做。
全部的事件响应器都在作相似的事情:从$_GET,$_POST(或者是$_REQUEST)中提取用户的请求参数,并作某些操做(文件系统,网络,或数据库相关的工做)而后返回处理结果。OO中很重要的一个特性就是继承,继承能够带来代码重用的好处:把公共的东西提取到父类中,子类只实现特定于本身的功能,这样就不用你不停的代码复制,也就符合了DRY原则了。
看看事件整个生命周期:用户动做致使事件————》事件从网络上传递到系统内部————》事件被事件分发器捕获————》事件分发器查询该事件对应的处理器————》事件分发器把流程转给响应的事件响应器————》事件响应器处理事件————》返回处理结果。
以上流程都是正常流,固然可能发生没有注册事件处理器或异常状况。以上的事件也就仅是一个字符串而已(固然事件的信息都在POST GET和COOKIE中)。
***
****
事件框架图:

上面的图抽象类没有画getPdo方法,还有参数问题,本身看着办吧,可要可不要反正php中参数信息都在全局数组中放着,全局数组($_GET,$_POST,$_COOKIE,$_REQUEST,$_SERVER,$_SESSION就是整个应用的通讯总线); 事件分发器须要具备注册和注销事件处理器的能力。全部的事件处理器都实现了同一个接口,为了方便在中间又引入了一个抽象类,这个类有个pdo属性,这个属性是子类共用的,惟独handle方法还须要被实例化(被具体类来实现)。 这样设计的目的是规范化编码,php是弱类型的语言,只要你实现了某个方法,就能够认为你是某个接口的实现者,这个要求比较宽泛,但为了严格起见,仍是声明本身实现了某个接口,注册时每每也会类型检查的!。 类细说: class Dispatcher { private $event; function __construct($eventStr){ $this- >event = $eventStr; } function handleEvent(){ $eventReactorClass = “{$this- >event}_Handler”; if (class_exists($eventReactorClass )){ $handler_obj = new $eventReactorClass ($this- >event); $response = $handler_obj->handle(); return $response; }else{ echo “I can’t handle this!”; } } } 上面的代码主要是根据表明事件的字符串来查找对应的事件响应类,并实例化一个事件处理对象,以后调用其方法handle,返回处理的结果便可。这里有不少策略能够用好比注册事件字串跟类的映射关系,事件处理器类所在的文件夹等均可以考虑,固然“惯例优于配置”已经被好多项目所接纳,因此全部的事件响应器类都以事件名很后缀_Handler结尾,方便了开发。因此如今流行的MVC框架都有一套命名惯例。 要扩充系统功能,只须要实现事件处理器的那个handle函数便可,类名中暗含有要处理哪一个事件的信息。至于该函数传递的参数,能够选择性用之,也能够忽略。固然通常事件分发器会把最完备的信息做为参数传递给事件响应器的。经常自定义一个Request Response类做为参数传入,Request即封装$_GET,$_POST,$_COOKIE或者$_REQUEST数组。Response做为响应数据的收集容器用 通常带有缓存性质。 IEventHandle接口类: interface IEventHandle { function handle(); } 提供通信协议,让全部的子类都听从此协议(就是实现那个handle方法)。 抽象类 EventHandler: abstract class EventHandler { private $pdo; function getPdo(){ $this->pdo = new PDO('配置串'); return $pdo; } abstract function handle(); } 主要为子类提供便捷的获取pdo或者数据库链接句柄的功能,这样就不用在子类中重复出现获取数据库链接这段代码。 固然能够根据项目须要随意添加公共资源在这个抽象类中。具体事件处理函数的实现仍是推迟到子类中了。 具体事件响应器类: class Delete_Handler extends EventHandler{ private $event; function __construct($event){ $this->event = $event; } //下面实现本类要完成的任务:参数看状况弄吧 function handle(...){ $targetId = $_REQUEST['id'];//获取删除的目标ID $pdo = parent::getPdo(); try{ //用pdo完成删除记录的操做 }catch(PDOException $ex){ throw $ex; } //或者用模板技术显示某个页面去 return true; } } **** ***** 引入安全: 一个操纵是否被容许执行,须要参照当前的上下文信息,上下文也指环境,即当前是哪一个行为者在触发事件; 常规的实现是这样的: performSomeMethod(){ $operator = $_SESSION['user']; if(!empty($operator) && $operator == 'someRole'){ //这里执行常规代码; }eles{ echo "你无权进行此项操做!"; } } 以上代码中主要编入了验证逻辑,判断用户是否登陆,而且是某个角色,而后符合要求后再执行正常的执行逻辑,若是这样的逻辑确实是每一个事件响应器必须执行的那么能够提取到抽象类中,并从新设计一个方法叫:secureHandle() 使之为抽象的强迫子类实现,并改写分发器:改成调用这个secureHandle()方法,这时接口可能也要改,在php中接口可能只是一个约定而已,若是不进行类型检查那么接口看似无关紧要。这样的方案我的感受不是太好,因此给出一个我认为还能够接受的方案: 改写抽象类: abstract class EventHandler { private $pdo; function getPdo(){ $this->pdo = new PDO('配置串'); return $pdo; } //使用模板方法设计模式 public function handle($eventContext){ try{ //若是有参数就传进来 $this->_before($eventContext); $response = $this->_handle($eventContext); $this->_after($eventContext); return $response; }catch(Exception $ex){ //是重抛仍是如何处理取决于你的异常处理策略 } } protected funciton _before(&$_context){ //这里作验证 不经过 就抛一个验证失败的异常。 } protected function _after(&$_context){ //这里随便作啥 或者日志吧! } //强迫子类实现这个方法 abstract function _handle(&$_context); } 通过以上改写,应用了模板方法设计模式 能够把验证提到_before操做中,把_handle方法设计为抽象的强迫子类实现 若是子类还想更改安全验证策略只须要覆写_before操做便可。这样原先的接口,事件分发器,都不须要改动。另外这里也用到了before/after设计模式。请本身查找相关资料。如今子类惟一要作的是复写_handle方法,必要时复写_before方法。 ***** ************** 结语: 还有许多问题值得考虑,这里只给出了大概的思路,好比GUI领域中经常出现的事件注册机制是否须要,主要用来根据事件查找对应的处理器,固然这个映射彻底能够来自配置文件,或者数据库。若是结合上访问控制列表技术,每个用户都有一个对应的可操做的事件处理器集,能够根据当前用户Id加载其全部可用的事件处理器列表,而后再调用相应处理器的方法,若是列表中找不到对应于事件的处理器证实当前用户没有这个权利。这个逻辑就能够提取到抽象类中来实现。 另外一个值得考虑的问题是类加载问题,一个方法是把系统可能用到的类文件提早所有include进来,这在小项目中是可行的,可是对于第三方库或者有多个文件夹且相互嵌套时 这种方法会很笨拙。另外一种方法是在事件分发器加载类以前从一个配置文件中读取相关类和类所在文件的映射信息,而后把类文件包含进来。还有一个就是zendFramework用的把文件夹加进类路径中include_path而后使用自动加载spl_autoload技术。我前面有一个本身写的自动加载类能够用的!