PHP的容器Container如何保证唯一?

前提:
我在试着实现一个Psr容器,有getsethasmake等方法,set就直接保存对象,get就判断存在则调用makemake根据对象是闭包还是实例依此反射函数或类,如果类的构造函数或闭包所需的函数列表里存在类,则继续调用make方法去获得实例。

例子1

class Foobar
{
    public function __construct(Container $ci)
    {
        echo "所需参数是具体的容器对象";
    }
}

例子2

class Foobar
{
    public function __construct(\Psr\Container\ContainerInterface $ci)
    {
        echo "所需参数是需实现了Psr容器接口的对象";
    }
}

一般容器的调用方式

$contaienr->make(Foobar::class);
// 不考虑下面这个方式
//$contaienr->make(Foobar::class, $container);

问题来了,如何在调用时不将$container实例传过去,却能保证容器对象始终是唯一的呢?

下面是我具体的容器代码,经测试一般的调用没什么问题,只是如果对象需要保证是唯一的单例,我就不知道该如何处理比较好。

<?php
declare(strict_types = 1);

namespace Hello;

use ReflectionClass;
use ReflectionFunction;
use ReflectionFunctionAbstract;
use Psr\Container\ContainerInterface;
use Hello\Exception\Exception;
use Hello\Exception\NotFoundException;

class Container implements ContainerInterface
{
    /**
     * 资源对象
     */
    private $resource = [];

    /**
     * 获取对象
     */
    public function get($id)
    {
        if ($this->has($id)) {
            return $this->make($id);
        } else {
            throw new NotFoundException("{$id} 没有找到!");
        }
    }

    /**
     * 设置对象
     */
    public function set($id, $object)
    {
        $this->resource[$id] = $object;
    }

    /**
     * 判断对象是否存在
     */
    public function has($id)
    {
        return array_key_exists($id, $this->resource);
    }

    /**
     * 删除对象
     */
    public function remove($id)
    {
        if ($this->has($id)) {
            unset($this->resource[$id]);
        } else {
            throw new NotFoundException("{$id} 没有找到!");
        }
    }

    /**
     * 创造对象
     */
    public function make($id, $userParams = [])
    {
        // 已经存在
        if (isset($this->resource[$id])) {
            return $this->resource[$id];
        }
        // 判断对象
        if (is_callable($id)) {
            // 函数
            return $this->call($id, $userParams);
        } else if (is_string($id) && class_exists($id)) {
            // 类名
            return $this->instance($id, $userParams);
        } else {
            // 其他
            return $id;
        }
    }

    /**
     * 参数
     */
    public function parameters($object, $userParams = [])
    {
        // 反射函数
        $func = $object instanceof ReflectionFunctionAbstract ? $object : new ReflectionFunction($object);
        // 参数处理
        $userParams = is_array($userParams) ? $userParams : [$userParams];
        // 函数名称
        $funName = $func->getName();
        // 函数所需参数
        $funParams = $func->getParameters();
        if (empty($funParams)) {
            return $userParams;
        }
        // 循环所需参数
        $args = [];
        foreach ($funParams as $key => $item) {
            $class = $item->getClass();
            $name = $item->getName();
            if (!empty($class)) {
                // 参数是对象,去实例化注入
                $args[] = $this->instance($class);
            }
        }
        return array_merge($args, $userParams);
    }

    /**
     * 实例
     */
    public function instance($class, $userParams = [])
    {
        // 反射类
        $ins = $class instanceof ReflectionClass ? $class : new ReflectionClass($class);
        // 是否可实例化
        if (!$ins->isInstantiable()) {
            throw new \Exception("{$ins->getName()} 无法实例化!");
        }
        // 构造函数
        $construct = $ins->getConstructor();
        // 解析参数
        $params = empty($construct) ? [] : $this->parameters($construct, $userParams);
        // 返回实例
        return $ins->newInstanceArgs($params);
    }

    /**
     * 函数
     */
    public function call($func, $params = [])
    {
        // 获取参数
        $params = $this->parameters($func, $params);
        return $func(...$params);
    }
}
阅读 2.4k
2 个回答

抱歉,需求理解错误。
目前的理解是,你有些对象需要单例,有些对象需要实例?

<?php
class Container {
    private $singletons = []; // id => 类名或单例
    private $definitions = []; // id => 类名
    
    public function get($id) {
        if(isset($definitions[$id])) {
            return make(); // 创建实例,创建过程自己实现
        }
        if(isset($singletons[$id]) && is_object($singletons[$id])) { // 已经是单例对象
            return $singletons[$id];
        }
        if(isset($singletons[$id])) {
            return $this->singletons[$id] = make(); // 创建单例,然后设置到单例数组,最后返回该单例
        }
        throw new ClassNotFound();
    }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏