• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

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

php 搞代码 3年前 (2022-02-28) 21次浏览 已收录 0个评论
文章目录[隐藏]

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

这篇文章可能文字不会太多,毕竟说再多都还不如间接看代码来的切实 😀,以下我会把外围的代码都先贴出来,外面都有比较完善的正文信息,能够对着看。另外如果本人测试的话,能够间接下载我的源码,对于如何测试,源码中都有示例代码。

  • Gitee 地址
  • GitHub 地址

以下是实现容器的外围代码

<code class="php">
<?php
/**
 * 实现一个简略的 php 容器
 *
 * Created by PhpStorm
 * User: Alex
 * Date: 2021-08-03 17:51
 * E-mail: <[email protected]>
 */

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;
    }

}



搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:如何使用-php-写一个类似于-laravel-框架的服务容器
喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址