当前位置: 首页 > article >正文

php容器设计模式

PHP中的容器设计模式

容器设计模式(Container Design Pattern)在PHP中是一种用于管理对象实例创建、配置和依赖注入的设计模式。这种设计模式的核心思想是将对象的创建和配置过程从应用中抽离出来,交由一个专门的容器类来管理。容器设计模式在PHP框架中被广泛使用,特别是在实现依赖注入(DI)和控制反转(IoC)时。

以下是一个简单的容器使用图

左边部分(Di 容器)

Di 类作为依赖注入容器,管理服务绑定、实例化等操作逻辑。

  • getInstance():获取 Di 类单例实例,确保应用只有一个容器实例运行。
  • get():便捷获取容器中指定服务对应的实例。
  • make():核心创建实例方法,依服务绑定情况(闭包或类等)创建实例。
  • bindTo():用于绑定服务,关联服务名称与具体实现(类、闭包、对象等)。
  • has():检测服务是否已在容器中绑定或实例化。
  • remove():卸载服务,移除容器相关列表里服务的绑定和实例信息。
  • delete():执行删除具体服务记录操作。

中间部分(ArrayAccess 接口)

Di 类实现 ArrayAccess 接口,按其规则让实例可像数组般被访问。

  • offsetExists():判断服务名称是否存在,通过调用 has 方法实现。
  • offsetGet():获取指定服务名称对应的实例,内部调用 make 方法。
  • offsetSet():设置服务绑定关系,实际调用 bindTo 方法操作。
  • offsetUnset():移除服务绑定等信息,调用 remove 方法执行。
  • getIterator():返回已实例化对象迭代器,便于遍历。
  • count():返回已实例化对象数量,统计存储实例数组的元素个数。

右边部分(具体实现类等)

此部分是服务绑定对应的具体实现内容。若绑定类名,容器用反射机制(如 newInstance 或 newInstanceArgs 操作)实例化;若绑定闭包,获取实例时执行闭包生成对应实例对象。

容器实现代码

<?php

use ArrayAccess;
use ArrayIterator;
use Closure;
use ReflectionClass;
use ReflectionException;

class Di implements ArrayAccess
{
    protected static $instance;
    protected $instances = []; // 已实例化类的存储数组
    protected $bind = []; // 服务列表,存储服务名称与对应的类、闭包等绑定关系

    // 单例模式,获取容器的唯一实例,防止多次实例化
    public static function getInstance()
    {
        if (self::$instance) {
            return self::$instance;
        }
        static::$instance = new static();
        return static::$instance;
    }

    // 获取指定抽象服务(类名等标识)对应的实例
    public static function get($abstract, $vars = [])
    {
        return static::getInstance()->make($abstract, $vars);
    }

    // 创建并返回指定抽象服务对应的实例,核心方法之一
    public function make($abstract, $vars = [])
    {
        // 先从已经实例化的列表中查找,若存在则直接返回该实例
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        // 检测有没有注册该服务,如果未注册则返回 null
        if (!isset($this->bind[$abstract])) {
            return null;
        }

        $concrete = $this->bind[$abstract]; // 获取对象具体注册内容

        $obj = null;
        // 若绑定的是匿名函数(闭包),则通过传入参数调用该闭包来创建实例
        if ($concrete instanceof Closure) {
            $obj = call_user_func_array($concrete, $vars);
        } elseif (is_string($concrete)) { // 如果是字符串,表示类名
            try {
                $class = new ReflectionClass($concrete);
                if (empty($vars)) {
                    // 无参数实例化类
                    $obj = $class->newInstance();
                } else {
                    // 带参数的类实例化,使用反射机制解析参数并创建实例
                    $obj = $class->newInstanceArgs($vars);
                }
            } catch (ReflectionException $e) {
                // 处理反射过程中可能出现的异常,比如类不存在等情况
                throw new \Exception("Error creating instance for '$abstract': ".$e->getMessage());
            }
        }

        // 将新创建的实例存储到已实例化列表中(如果需要单例模式,后续获取会直接返回此实例)
        $this->instances[$abstract] = $obj;

        return $obj;
    }

    // 绑定服务,可以传入多种形式的参数来建立服务与对应实现的绑定关系
    public function bindTo($abstract, $concrete = null)
    {
        if (is_array($abstract)) {
            // 如果传入的是数组,合并到已有的服务绑定列表中
            $this->bind = array_merge($this->bind, $abstract);
        } elseif ($concrete instanceof Closure) {
            // 如果传入的是闭包,将服务名称与闭包绑定
            $this->bind[$abstract] = $concrete;
        } elseif (is_object($concrete)) {
            // 如果传入的是对象,将其作为对应服务的实例存储(可用于单例等情况)
            if (isset($this->bind[$abstract])) {
                $abstract = $this->bind[$abstract];
            }
            $this->instances[$abstract] = $concrete;
        } else {
            // 普通的服务名称与类名(字符串形式)绑定
            $this->bind[$abstract] = $concrete;
        }

        return $this;
    }

    // 检测是否已经绑定了指定的服务(通过服务名称判断)
    public function has($abstract)
    {
        return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
    }

    // 卸载指定的服务,从已绑定和已实例化的相关列表中移除
    public function remove($abstract)
    {
        return static::getInstance()->delete($abstract);
    }

    public function delete($abstract)
    {
        unset($this->instances[$abstract]);
        unset($this->bind[$abstract]);
        return true;
    }

    // 实现 ArrayAccess 接口的方法,用于判断指定偏移量(服务名称)是否存在
    public function offsetExists($offset)
    {
        return $this->has($offset);
    }

    // 实现 ArrayAccess 接口的方法,用于获取指定偏移量(服务名称)对应的实例
    public function offsetGet($offset)
    {
        return $this->make($offset);
    }

    // 实现 ArrayAccess 接口的方法,用于设置指定偏移量(服务名称)与对应的值(绑定关系等)
    public function offsetSet($offset, $value)
    {
        $this->bindTo($offset, $value);
    }

    // 实现 ArrayAccess 接口的方法,用于移除指定偏移量(服务名称)的绑定关系等
    public function offsetUnset($offset)
    {
        return $this->remove($offset);
    }

    // 实现可迭代接口的方法,返回已实例化对象的迭代器,方便遍历已实例化的对象
    public function getIterator()
    {
        return new ArrayIterator($this->instances);
    }

    // 实现可数接口的方法,返回已实例化对象的数量
    public function count()
    {
        return count($this->instances);
    }
}

http://www.kler.cn/a/468044.html

相关文章:

  • C++11——2:可变模板参数
  • 25/1/6 算法笔记<强化学习> 初玩V-REP
  • 【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现
  • Leetcode打卡:设计一个ATM机器
  • 【软考网工笔记】计算机基础理论与安全——网络安全
  • javaEE-网络编程-3 UDP
  • 050_小驰私房菜_MTK Camera debug, data rate 、mipi_pixel_rate 确认
  • 基于图的去中心化社会推荐过滤器
  • ip属地的信息准确吗?ip归属地不准确怎么办
  • 前端实现大文件上传(文件分片、文件hash、并发上传、断点续传、进度监控和错误处理,含nodejs)
  • 抖音评论区的IP属地可以关吗?详细解答
  • 安卓应用4字节不对齐导致so加载失败
  • javaEE-文件内容的读写
  • MySQL--》快速提高查询效率:SQL语句优化技巧与实践
  • 开源:软件世界的革命者
  • Windows远程--如何使用IP访问服务器
  • 桌面开发 的设计模式(Design Patterns)基础知识
  • 【Java回顾】Day4 异常机制
  • Ruby 数据类型
  • Unity 从零开始的框架搭建1-3 关于命令模式的一些思考
  • 四、VSCODE 使用GIT插件
  • 综合能源建模:理论、方法与实践
  • 行为模式3.迭代器模式
  • rsync命令常用同步方案
  • (leetcode算法题)528. 按权重随机选择
  • 记录一下core-js安装报错(core-js/modules/es.array.push.js core-js/modules/es.error.cause.js)