是为业余时间写文章而写的Pimple源码阅读笔记。疙瘩码有两种编码方式,一种是用PHP写的,一种是用C扩展写的。当然个人能力有限,先看第一个吧。Pimple链接官网WebSiteGitHub-PimplePimple中文版文档必备知识ArrayAccess(数组访问)接口提供了一个接口,提供像数组一样访问对象的能力。http://php.net/manual/zh/clas...一个Class只要实现以下4个接口,就可以像数组一样操作Object。ArrayAccess{/*方法*/abstractpublicbooleanoffsetExists(mixed$offset)abstractpublicmixedoffsetGet(mixed$offset)abstractpublicvoidoffsetSet(mixed$offset,mixed$value)abstractpublicvoidoffsetUnset(mixed$offset)}伪代码以下类A实现\ArrayAccess{//实现4个接口}$a=newA();//你可以这样做$a['x']='x';//对应offsetSetecho$a['x'];//对应offsetGetvar_dump(isset($a['x']));//对应offsetExistsunset($a['x']);//对应offsetUnset特殊说明,只支持以上四种操作,不要以为自己实现了ArrayAccess就可以用foreach了。要实现loop=iteration,就必须实现Iterator(迭代器)接口。其实PHP定义了很多预定义的接口,大家有空可以看看。SPL——SplObjectStorageSPLSPL是StandardPHPLibrary(PHP标准库)的缩写,是为解决标准问题而设计的接口和类的集合。SPL提供了一套标准数据结构,一套遍历对象的迭代器,一套接口,一套标准异常,一系列处理文件的类,一套函数。有关详细信息,请参阅文档。SplObjectStorageSplObjectStorage是SPL标准库中的一种数据结构对象容器,用于存储一组对象,尤其是需要唯一标识对象时。SplObjectStorageimplementsCountable,Iterator,Serializable,ArrayAccess{/**给SplObjectStorage添加一个对象,$data为可选参数*因为SplObjectStorage实现了ArrayAccess接口,所以可以以数组的形式访问,相当于设置了object作为数组键,data为对应的值,默认data为null*/publicvoidattach(object$object[,mixed$data=NULL])/**检查SplObjectStorage是否包含object,相当于isset判断*/publicboolcontains(object$object)/**从SplObjectStorage中移除object,相当于unset*/publicvoiddetach(object$object)//其他接口定义可以自行查看文档}SplObjectStorageimplementsCountable、Iterator、Serializable、ArrayAccess四个接口,可以实现统计、迭代、序列化、数组访问等功能,其中Iterator和ArrayAccess已经是int以上产生。魔术方法__invoke()__invoke()当尝试以与函数调用相同的方式调用对象时,自动调用__invoke()方法。我们来看一个例子,一目了然。factories=new\SplObjectStorage();$this->protected=new\SplObjectStorage();foreach($valuesas$key=>$value){$this->offsetSet($key,$value);}}公共函数offsetSet($id,$value){}公共函数offsetGet($id){}公共函数offsetExists($id){}公共函数offsetUnset($id){}公共函数工厂($callable){}publicfunctionprotect($callable){}publicfunctionraw($id){}publicfunctionextend($id,$callable){}publicfunctionkeys(){}publicfunctionregister(ServiceProviderInterface$provider,array$values=array()){}}Container实现了ArrayAccess接口,这就可以理解为什么可以用数组的形式来定义服务了。重要函数分析1、offsetSet、offsetExists、offsetUnset主要实现ArrayAccess的接口,简单易懂2、factory和protect的主要逻辑是判断传入的$callable是否有__invoke。如果有,通过SplObjectStorage::attach,存储在对象中3.Raw获取设置的原始内容4.Key获取所有keys5.register()注册一些通用服务6。offsetGet()publicfunctionoffsetGet($id){if(!isset($this->keys[$id])){//如果没有设置,抛出newUnknownIdentifierException($id);}if(isset($this->raw[$id])//raw中已经有一个值了,一般来说,之前已经获取过一次实例,再次获取时,返回的还是一样的value||!\is_object($this->values[$id])//对应的值不是对象,是普通值||isset($this->protected[$this->values[$id]])//存在于protected||!\method_exists($this->values[$id],'__invoke')//对应的值不是封闭包){return$this->values[$id];//返回values数组中的值}if(isset($this->factories[$this->values[$id]])){//如果factory方法中设置了相关方法返回$this->值[$id]($this);//直接调用这个方法,传入参数($this),即匿名函数中可以访问当前实例的其他服务}$raw=$this->values[$id];$val=$this->values[$id]=$raw($this);//初始化通用服务,传入($this),稍后调用得到同一个实例$this->raw[$id]=$raw;//将原始内容存储在原始数组中$this->frozen[$id]=true;//初始化后冻结key,不能被覆盖return$val;}7.extend()扩展一个服务,如果已经冻结则不能扩展和上面说的直接覆盖还是有区别的。直接覆盖意味着之前定义的服务被完全忽略。使用extend可以对原始定义进行更改。publicfunctionextend($id,$callable){//...省略了一些判断逻辑//如果是受保护的服务,目前还不支持extendif(isset($this->protected[$this->values[$id]])){@\trigger_error(\sprintf('Pimple在扩展受保护闭包时的行为方式将在Pimple4中得到修复。你确定“%s”应该受到保护吗?',$id),\E_USER_DEPRECATED);}if(!\is_object($callable)||!\method_exists($callable,'__invoke')){thrownewExpectedInvokableException('扩展服务定义不是闭包或可调用对象。');}$factory=$this->values[$id];//主要是这两行代码$extended=function($c)use($callable,$factory){return$callable($factory($c),$c);};如果(isset($this->factories[$factory])){$this->factories->detach($factory);$this->f演员->附加($扩展);}返回$this[$id]=$extended;}还有一篇未完待续,主要是关于PSR11兼容性的。原创文章,欢迎转载。转载请注明出处,谢谢。原文链接地址:http://dryyun.com/2018/04/18/...作者:dryyun发表日期:2018-04-1814:36:40
