什么是容器在开发过程中,经常用到的一个概念就是依赖注入。我们使用惰性注入来解耦代码,选择性地按需加载服务,而这些通常都是借助容器来实现的。容器实现了对象的统一管理,保证了对象实例的唯一性。容器很容易找到很多实现例子,比如PHP-DI、YII-DI等实现。通常它们要么是大而全,要么是适应性强的具体业务与实际需求相冲突。迫于需要,我们自己做了一个轻量化的轮子,为了保持规范,我们基于PSR-11实现了它。PSR-11PSR是php-fig提供的标准化提案,虽然不是官方组织,但得到广泛认可。PSR-11提供了一个容器接口。包含ContainerInterface和两个异常接口,并提供使用建议。/***描述了一个容器的接口,该接口公开了读取其条目的方法。*/interfaceContainerInterface{/***通过标识符查找容器的条目并返回它。**@paramstring$id要查找的条目的标识符。**@throwsNotFoundExceptionInterface找不到**this**标识符的条目。*@throwsContainerExceptionInterface检索条目时出错。**@return混合条目。*/公共函数get($id);/***如果容器可以返回给定标识符的条目,则返回true。*否则返回假。**`has($id)`返回true并不意味着`get($id)`不会抛出异常。*然而,它确实意味着`get($id)`不会抛出`NotFoundExceptionInterface`。**@paramstring$id要查找的条目的标识符。**@returnbool*/publicfunctionhas($id);}实例展示我们先来实例接口中要求的两个方法abstract类AbstractContainer实现ContainerInterface{protected$resolvedEntries=[];/***@vararray*/protected$definitions=[];公共函数__construct($definitions=[]){foreach($definitionsas$id=>$definition){$this->injection($id,$definition);}}publicfunctionget($id){if(!$this->has($id)){thrownewNotFoundException("Noentryorclassfoundfor{$id}");}$instance=$this->make($id);返回$实例;}publicfunctionhas($id){returnisset($this->definitions[$id]);其实注入到我们容器中的对象是多种多样的,所以我们分别抽取实例化方法protectedfunctionmake($name){if(isset($this->resolvedEntries[$name])){返回$this->resolvedEntries[$name];}$definition=$this->definitions[$name];$参数=[];如果(is_array($definition)&&isset($definition['class'])){$params=$definition;$definition=$definition['类'];取消设置($params['class']);}$object=$this->reflector($definition,$params);返回$this->resolvedEntries[$name]=$object;}publicfunctionreflector($concrete,array$params=[]){if($concreteinstanceof\Closure){return$concrete($params);}elseif(is_string($concrete)){$reflection=new\ReflectionClass($concrete);$dependencies=$this->getDependencies($reflection);foreach($paramsas$index=>$value){$dependencies[$index]=$va卢;}返回$reflection->newInstanceArgs($dependencies);}elseif(is_object($concrete)){return$concrete;}}/***@param\ReflectionClass$reflection*@returnarray*/privatefunctiongetDependencies($reflection){$dependencies=[];$constructor=$reflection->getConstructor();如果($constructor!==null){$parameters=$constructor->getParameters();$dependencies=$this->getParametersByDependencies($parameters);}返回$dependencies;}/****获取构造类相关参数的依赖*@paramarray$dependencies*@returnarray$parameters**/privatefunctiongetParametersByDependencies(array$dependencies){$parameters=[];foreach($dependenciesas$param){if($param->getClass()){$paramName=$param->getClass()->name;$paramObject=$this->反射器($paramName);$参数[]=$参数对象;}elseif($param->isArray()){if($param->isDefaultValueAvailable()){$parameters[]=$param->getDefaultValue();}else{$参数[]=[];}}elseif($param->isCallable()){if($param->isDefaultValueAvailable()){$parameters[]=$param->getDefaultValue();}else{$parameters[]=function($arg){};}}else{if($param->isDefaultValueAvailable()){$parameters[]=$param->getDefaultValue();}else{if($param->allowsNull()){$parameters[]=null;}else{$参数[]=错误的;}}}}返回$参数;}可以看到,到目前为止我们只实现了从容器中取出实例,这里要提供实例定义,所以我们还需要提供一个方法。/***@param字符串$id*@param字符串|数组|callable$concrete*@throwsContainerException*/publicfunctioninjection($id,$concrete){if(!is_string($id)){thrownew\InvalidArgumentException(sprintf('id参数必须是字符串类型,%s给定',is_object($id)?get_class($id):gettype($id)));}if(is_array($concrete)&&!isset($concrete['class'])){thrownewContainerException('数组必须包含类定义');}$this->definitions[$id]=$concrete;}这是唯一的方法吗?是的,通过这些操作,我们已经有了一个完整的容器,可以开箱即用,但是为了方便使用,我们可以提供一些方便的方法,比如数组访问。类ContainerextendsAbstractContainerimplements\ArrayAccess{publicfunctionoffsetExists($offset){return$this->has($offset);}publicfunctionoffsetGet($offset){return$this->get($offset);}publicfunctionoffsetSet($offset,$value){return$this->injection($offset,$value);}publicfunctionoffsetUnset($offset){unset($this->resolvedEntries[$offset]);取消设置($this->definitions[$offset]);}}这样,我们就有了一个功能丰富、简单易用的轻量级容器,并快速集成到你的项目中。点击此处查看完整代码
