模拟服务容器Ioc
服务容器是一个用于管理类依赖和执行依赖注入的强大工具。
一个类要被容器所能够提取,必须要先注册至这个容器。既然称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西就是服务提供器(ServiceProvider)。
依赖注入和控制反转是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述,应用程序依赖容器创建并注入它所需要的外部资源。而控制反转是从容器的角度在描述,容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
<?php /** * Interface Log * 面向接口编程 */ interface Log { public function write(); } class FileLog implements Log { public function write() { echo 'filelog write...' . PHP_EOL; } } class DataBaseLog implements Log { public function write() { echo 'dblog write...' . PHP_EOL; } } /** * Class Request * 模拟请求类 */ class Request { public function toArray() { return ['name' => 'value']; } } /** * User类依赖Log接口的实现 */ class User { private $log; private $extra; public function __construct(Log $log, $a, $b, $c = '默认参数') { $this->log = $log; $this->extra = compact('a', 'b', 'c'); } /** * 模拟用户登录写入登录日志 */ public function login(Request $request) { echo '接收登录请求的参数json:' . json_encode($request->toArray()) . PHP_EOL; echo 'user log success...' . PHP_EOL; $this->log->write(); } public function getExtra() { var_dump($this->extra); } } /** * Class Ioc * 模拟IoC容器 * 类从注册到实例化,最终被我们所使用,都是服务容器负责 */ class Ioc { protected $bindings = []; protected $instances = []; protected static $ioc; protected function __construct() { } public static function getInstance() { if (is_null(self::$ioc)) { self::$ioc = new self(); } return self::$ioc; } /** * 注册绑定 (绑定自身、闭包、接口) * 也就是服务 * @param $abstract * @param null $concrete * @param bool $share */ public function bind($abstract, $concrete = null, $share = true) { if (is_null($concrete)) { $concrete = $abstract; } $this->bindings[$abstract]['share'] = $share; if ($concrete instanceof Closure) { $this->bindings[$abstract]['concrete'] = $concrete; } else { $this->bindings[$abstract]['concrete'] = function (Ioc $ioc, $vars = []) use ($concrete) { return $ioc->build($concrete, $vars); }; } } /** * 返回对象 * @param $abstract * @param array $vars * @return mixed * @throws ReflectionException */ public function make($abstract, $vars = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } if (isset($this->bindings[$abstract]['concrete'])) { $concrete = $this->bindings[$abstract]['concrete']; $instance = $concrete($this, $vars); if ($this->bindings[$abstract]['share']) { $this->instances[$abstract] = $instance; } return $instance; } throw new RuntimeException($abstract . ' is not bound yet'); } /** * 创建对象 * @param $concrete * @return object * @throws ReflectionException */ public function build($concrete, $vars = []) { $reflectionClass = new ReflectionClass($concrete); $constructor = $reflectionClass->getConstructor(); if (is_null($constructor)) { return $reflectionClass->newInstance(); } $isInstantiable = $reflectionClass->isInstantiable(); if (!$isInstantiable) { throw new ReflectionException("{$concrete} cant construct"); } $parameters = $constructor->getParameters(); $dependencies = $this->getDependencies($parameters, $vars); return $reflectionClass->newInstanceArgs($dependencies); } /** * 获取参数的依赖 * @param array $parameters * @return array * @throws ReflectionException */ public function getDependencies(array $parameters, $vars = []) { $dependencies = []; reset($vars); $type = key($vars) === 0 ? 'figure' : 'letter'; /** * @var ReflectionParameter $parameter */ foreach ($parameters as $parameter) { $dependency = $parameter->getClass(); $name = $parameter->getName(); if ($dependency) { $dependencies[] = $this->getObjectParam($dependency->getName(), $vars); } elseif ('figure' == $type && !empty($vars)) { $dependencies[] = array_shift($vars); } elseif ('letter' == $type && isset($vars[$name])) { $dependencies[] = $vars[$name]; } elseif ($parameter->isDefaultValueAvailable()) { $dependencies[] = $parameter->getDefaultValue(); } else { throw new ReflectionException('The constructor of the ' . $parameter->getDeclaringClass()->getName() . ' class has no default value of $' . $name); } } return $dependencies; } /** * 获取对象类型的参数值 * @access protected * @param string $className 类名 * @param array $vars 参数 * @return mixed */ protected function getObjectParam($className, &$vars) { $array = $vars; $value = array_shift($array); if ($value instanceof $className) { $result = $value; array_shift($vars); } else { $result = $this->make($className); } return $result; } /** * @param null $key * @return array|mixed|null */ public function getInstances($key = null) { if (is_null($key)) { return $this->instances; } elseif (isset($this->instances[$key])) { return $this->instances[$key]; } else { return null; } } } // run /* | 模拟容器绑定 */ $ioc = Ioc::getInstance(); $ioc->bind(Request::class);//绑定类 $ioc->bind(Log::class, FileLog::class);//绑定接口 //$ioc->bind(Log::class, DataBaseLog::class); $ioc->bind('test', function () {//绑定闭包 return 'test' . PHP_EOL; }); /* | 模拟路由访问 User 控制器下的 login 方法 */ $ioc->bind(User::class, null, true); $method = 'login'; $reflectionMethod = new ReflectionMethod(User::class, $method); $parameters = $reflectionMethod->getParameters(); $dependencies = $ioc->getDependencies($parameters); $user = $ioc->make(User::class, ['参数1', '参数2']); call_user_func_array([$user, $method], $dependencies); // 查看参数 $user->getExtra(); var_dump($ioc->getInstances());
分情破爱始乱弃,流落天涯思别离。
如花似玉负情意,影如白昼暗自迷。
随风浮沉千叶落,行色匆匆鬓已稀。