如何使用 php 写一个类似于 laravel 框架的服务容器()

如何使用 php 写一个类似于 laravel 框架的服务容器?

这篇文章可能文字不会太多,毕竟说再多都还不如直接看代码来的实在 ,以下我会把核心的代码都先贴出来,里面都有比较完善的注释信息,可以对着看。另外如果自己测试的话,可以直接下载我的源码,关于如何测试,源码中都有示例代码。
  • Gitee 地址
  • GitHub 地址
以下是实现容器的核心代码
*/class Container {/** * 当前全局可用的容器(如果有) * * @var static */ private static $instance; /** * 容器的绑定 * * @var array[] */ private $bindings = []; /** * 容器的共享实例 * * @var object[] */ private $instances = []; public function __construct() { $this->instances[Container::class] = $this; }public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new self; }self::$instance->instances[Container::class] = self::$instance; return self::$instance; }/** * 在容器中注册共享绑定 * * @param $abstract * @param $concrete */ public function singleton($abstract, $concrete) { $this->bind($abstract, $concrete, true); }/** * 向容器注册绑定 * * @param $abstract * @param $concrete * @param false $shared */ public function bind($abstract, $concrete, $shared = false) { if ($concrete instanceof Closure) { $this->bindings[$abstract] = compact('concrete', 'shared'); } else { if (! is_string($concrete) || ! class_exists($concrete)) { throw new InvalidArgumentException('Argument 2 must be callback or class.'); } }$this->bindings[$abstract] = compact('concrete', 'shared'); }/** * 将现有实例注册为容器中的共享实例 * * @param string $abstract * @param mixed $instance * @return mixed */ public function instance($abstract, $instance) { $this->instances[$abstract] = $instance; return $instance; }/** * 从容器解析给定类型 * * @param string $abstract目标类的名称 * @param array $parameters实例化目标类时所需要的参数(非对象类型约束参数数组) * @return mixed|object */ public function make(string $abstract, array $parameters = []) {if (! isset($this->instances[$abstract]) && ! isset($this->bindings[$abstract])) { if (! class_exists($abstract)) throw new InvalidArgumentException("Target class [$abstract] does not exist."); }if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; }try {if (isset($this->bindings[$abstract])) { $concrete = $this->bindings[$abstract]['concrete']; if (is_callable($concrete)) { $instance = $this->resolveCallable($concrete, $parameters); } else { $instance = $this->resolveClass($concrete, $parameters); } } else { $instance = $this->resolveClass($abstract, $parameters); }if (isset($this->bindings[$abstract]) && $this->bindings[$abstract]['shared']) { $this->instances[$abstract] = $instance; }return $instance; } catch (\Exception $exception) { echo($exception->getMessage() . PHP_EOL); print_r($exception->getTraceAsString()); }}/** * 解决回调函数时的依赖 * * @param callable $callbackName目标回调函数 * @param array $realArgs * @return mixed * @throws ReflectionException */ private function resolveCallable(callable $callbackName, array $realArgs = []) { $reflector = new ReflectionFunction($callbackName); // 获取回调函数的参数列表 $parameters = $reflector->getParameters(); $list = []; if (count($parameters) > 0) { $list = $this->resolveDependencies($parameters, $realArgs); }// 调用函数参数 return $reflector->invokeArgs($list); }/** * 解决对象时的依赖 * * @param string|object $className目标类的名称 * @param array $realArgs * @return object目标类对应的实例对象 * @throws ReflectionException */ private function resolveClass($className, array $realArgs = []) { try { // 对目标类进行反射(解析其方法、属性) $reflector = new ReflectionClass($className); } catch (ReflectionException $e) { throw new RuntimeException("Target class [$className] does not exist.", 0, $e); }if (! $reflector->isInstantiable()) {// 检查类是否可以实例化 throw new RuntimeException("Target class [$className] is not instantiable."); }// 获取目标类的构造函数,当类不存在构造函数时返回 null $constructor = $reflector->getConstructor(); // 没有构造函数,则直接实例化 if (is_null($constructor)) { // return new $className; // 或者也可以直接这样去实例化,因为目标类没有构造函数,不需要传参数 return $reflector->newInstance(); }// 获取构造函数的参数列表 $parameters = $constructor->getParameters(); // 递归解析构造函数的参数 $list = $this->resolveDependencies($parameters, $realArgs); // 从给出的参数创建一个新的类实例 return $reflector->newInstanceArgs($list); }/** * 递归解析依赖树 * * @param array $dependencies目标类的构造函数参数列表 * @param array $parameters实例化目标类时的其他参数(非类型提示参数) * @return array实例化目标类时构造函数所需的所有参数 */ private function resolveDependencies(array $dependencies, array $parameters = []) { // 用于存储所有的参数 $results = []; foreach ($dependencies as $dependency) {// 获取类型提示类 $obj = $dependency->getClass(); // 如果类为 null,则表示依赖项是字符串或其他类型 if (is_null($obj)) {$parameterName = $dependency->getName(); // 获取参数的名称// 检查参数是否有默认值 if (! $dependency->isDefaultValueAvailable()) { if (! isset($parameters[$parameterName])) { throw new RuntimeException($parameterName . ' has no value'); } else { $results[] = $parameters[$parameterName]; } } else {// 参数有默认值的时候 if (isset($parameters[$parameterName])) { $results[] = $parameters[$parameterName]; } else { $results[] = $dependency->getDefaultValue(); // 获取参数的默认值 } }} else {// 类型提示确定是一个类时,则需要递归处理依赖项 $objName = $obj->getName(); // 获取依赖项的类名 if (! class_exists($objName)) { throw new RuntimeException('Unable to load class: ' . $objName); } else { $results[] = $this->make($objName); } }}return $results; }}

    推荐阅读