{"id":15025953,"url":"https://github.com/lmsail/php-ioc-facade","last_synced_at":"2026-02-08T20:31:03.267Z","repository":{"id":126047215,"uuid":"297654689","full_name":"lmsail/php-ioc-facade","owner":"lmsail","description":"探索 PHP 设计模式之IOC容器+Facade门面模式，使系统更加灵活，高度自定义...","archived":false,"fork":false,"pushed_at":"2020-09-23T06:25:50.000Z","size":20,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-13T01:41:26.271Z","etag":null,"topics":["facade","ioc","php72"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lmsail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-22T13:17:42.000Z","updated_at":"2020-09-23T06:29:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"aa67b49b-b31e-4842-aca3-b5b15819ce1d","html_url":"https://github.com/lmsail/php-ioc-facade","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lmsail/php-ioc-facade","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmsail%2Fphp-ioc-facade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmsail%2Fphp-ioc-facade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmsail%2Fphp-ioc-facade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmsail%2Fphp-ioc-facade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lmsail","download_url":"https://codeload.github.com/lmsail/php-ioc-facade/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lmsail%2Fphp-ioc-facade/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269373109,"owners_count":24406321,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-08T02:00:09.200Z","response_time":72,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["facade","ioc","php72"],"created_at":"2024-09-24T20:03:22.538Z","updated_at":"2026-02-08T20:31:02.750Z","avatar_url":"https://github.com/lmsail.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 探索 PHP 设计模式之IOC容器+门面模式\n\n---\n\n### 整体架构（目录结构）\n\n```txt\n├── App\n│   ├── Controller\t\t\t\t\t# 控制器\n│   │   ├── TestOne.php\n│   │   └── TestTwo.php\n│   ├── Facade\t\t\t\t\t\t\t# 门面类\n│   │   ├── Cache.php\n│   │   └── Facade.php\n│   ├── Library\t\t\t\t\t\t\t# 类库\n│   │   ├── Cache\n│   │   └── Psr\n│   ├── Container.php\t\t\t\t# 容器类\n│   └── Provider.php\t\t\t\t# 定义载入容器文件\n├── index.php\t\t\t\t\t\t\t\t# 入口文件\n```\n\n### IOC 容器（Inversion of Control）控制反转\n\n\u003e 概念：遵循依赖倒置原则的一种代码设计方案，依赖的创建 (控制) 由主动变为被动 (反转)。\n\u003e\n\u003e 目的：代码解耦，提高代码的可维护性\n\u003e\n\u003e 下面是具体实现的代码，Controller 没什么好说的，就是简单创建的类，并定义 test 方法\n\n**IOC容器类（Container.php）**\n\n\u003e “借（chao）鉴（xi）”的 Thinkphp6 IOC容器\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App;\n\nuse InvalidArgumentException;\nuse ReflectionClass;\nuse ReflectionException;\nuse ReflectionFunctionAbstract;\n\nclass Container\n{\n    /**\n     * 容器对象实例\n     * @var static\n     */\n    protected static $instance;\n\n    /**\n     * 容器中的对象实例\n     * @var array\n     */\n    protected $instances = [];\n\n    /**\n     * 容器绑定标识，别名 =\u003e 类实例对象\n     * @var array\n     */\n    protected $bind = [];\n\n    /**\n     * 获取当前容器的实例（单例）\n     * @access public\n     * @return static\n     */\n    public static function getInstance()\n    {\n        if (is_null(self::$instance)) {\n            self::$instance = new static;\n        }\n        return self::$instance;\n    }\n\n    /**\n     * 绑定一个类、实例、接口实现到容器\n     * @access public\n     * @param string|array $abstract 类标识、接口\n     * @param mixed        $concrete 要绑定的类、闭包或者实例\n     */\n    public function register($abstract, $concrete = null): Container\n    {\n        if ( is_array($abstract) ) {\n            foreach ($abstract as $alias =\u003e $item) {\n                $this-\u003eregister($alias, $item);\n            }\n        } else {\n            $abstract = $this-\u003egetAlias($abstract);\n            $this-\u003ebind[$abstract] = $concrete;\n        }\n        return $this;\n    }\n\n    /**\n     * 根据别名获取真实类名\n     * @param  string $abstract\n     * @return string\n     */\n    public function getAlias(string $abstract): string\n    {\n        if (isset($this-\u003ebind[$abstract])) {\n            $bind = $this-\u003ebind[$abstract];\n            if (is_string($bind)) {\n                return $this-\u003egetAlias($bind);\n            }\n        }\n        return $abstract;\n    }\n\n    /**\n     * 获取容器内的类实例对象/创建类对象\n     * @access public\n     * @param string $abstract 者标识\n     * @param array $vars 变量\n     * @throws\n     */\n    public function make(string $abstract, array $vars = [])\n    {\n        $abstract = $this-\u003egetAlias($abstract);\n\n        if (isset($this-\u003einstances[$abstract])) {\n            return $this-\u003einstances[$abstract];\n        }\n\n        $object = $this-\u003einvokeClass($abstract, $vars);\n        $this-\u003einstances[$abstract] = $object;\n        return $object;\n    }\n\n    /**\n     * 调用反射执行类的实例化 支持依赖注入\n     * @access public\n     * @param string $class 类名\n     * @param array $vars 参数\n     * @return mixed\n     * @throws \\Exception\n     */\n    public function invokeClass(string $class, array $vars = [])\n    {\n        try {\n            $reflect = new ReflectionClass($class);\n        } catch (ReflectionException $e) {\n            throw new \\Exception('class not exists: ' . $class);\n        }\n        $constructor = $reflect-\u003egetConstructor();\n        $args = $constructor ? $this-\u003ebindParams($constructor, $vars) : [];\n        return $reflect-\u003enewInstanceArgs($args);\n    }\n\n    /**\n     * 绑定参数\n     * @access protected\n     * @param ReflectionFunctionAbstract $reflect 反射类\n     * @param array                      $vars    参数\n     * @return array\n     */\n    protected function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array\n    {\n        if ($reflect-\u003egetNumberOfParameters() == 0) {\n            return [];\n        }\n        // 判断数组类型 数字数组时按顺序绑定参数\n        reset($vars);\n        $type   = key($vars) === 0 ? 1 : 0;\n        $params = $reflect-\u003egetParameters();\n        $args   = [];\n        foreach ($params as $param) {\n            $class = $param-\u003egetClass();\n            if ($class) {\n                $args[] = $this-\u003egetObjectParam($class-\u003egetName(), $vars);\n            } elseif (1 == $type \u0026\u0026 !empty($vars)) {\n                $args[] = array_shift($vars);\n            } else {\n                throw new InvalidArgumentException('method param miss:' . $param-\u003egetName());\n            }\n        }\n        return $args;\n    }\n\n    /**\n     * 获取对象类型的参数值\n     * @access protected\n     * @param string $className 类名\n     * @param array  $vars      参数\n     * @return mixed\n     */\n    protected function getObjectParam(string $className, array \u0026$vars)\n    {\n        $array = $vars;\n        $value = array_shift($array);\n        if ($value instanceof $className) {\n            $result = $value;\n            array_shift($vars);\n        } else {\n            $result = $this-\u003emake($className);\n        }\n        return $result;\n    }\n}\n```\n\n**容器Provider定义文件（Provider.php）**\n\n```php\n\u003c?php\n/**\n * 容器Provider定义文件\n * 别名 =\u003e 类实例对象\n */\n\nuse App\\Controller\\TestOne;\nuse App\\Library\\Cache\\Redis;\n\nreturn [\n    'testOne' =\u003e TestOne::class,\n    'cache'   =\u003e Redis::class, # 这里可灵活配置cache的指向\n];\n```\n\n**入口文件（index.php）**\n\n```php\n\u003c?php\n/**\n * 心得：结合IOC容器+Facade门面模式使得系统更加灵活，可高度自定义，灵活配置\n */\nrequire __DIR__ . '/vendor/autoload.php';\n\nuse App\\Container;\nuse App\\Facade\\Cache;\n\n$provider = include __DIR__ . '/App/Provider.php';\n\n# 将 Provider 中定义的类绑定到容器内\n$container = Container::getInstance()-\u003eregister($provider);\n\n# $container = $container-\u003emake('testOne')-\u003etest();\n\n# 通过别名 cache 获取实例对象并调用 set 方法\necho $container-\u003emake('cache')-\u003eset('test', '123') . PHP_EOL;\n\n# 使用门面模式直接使用 Cache 实例对象调用 set 方法\necho Cache::set('test2', '456') . PHP_EOL;\n```\n\n---\n\n### 依赖注入（DI）\n\n\u003e 概念：通过参数的方式从外部传入依赖，将依赖的创建由主动变为被动 (实现了控制反转)。\n\n**雏形**\n\n```php\n\u003c?php\nclass A {\n    public $count = 10;\n}\nclass B {\n    public $_object;\n    public function __construct(A $a)\n    {\n        $this-\u003e_object = $a;\n    }\n    public function testTwo() {\n        return $this-\u003e_object-\u003ecount;\n    }\n}\n$b = new B((new A()));\necho $b-\u003etestTwo();\n```\n\n**有了IOC容器，我们可以实现自动注入依赖**\n\n```php\n\u003c?php\nclass A {\n    public $count = 10;\n}\nclass B {\n    public $_object;\n    public function __construct(A $a)\n    {\n        $this-\u003e_object = $a;\n    }\n    public function testTwo() {\n        return $this-\u003e_object-\u003ecount;\n    }\n}\n# 注册容器\n$container = Container::getInstance()-\u003eregister('a', A::class);\n# 得到 A 实例对象并调用 testTwo 方法\necho $container-\u003emake('a')-\u003etestTwo();\n```\n\n---\n\n### 外观模式（Facade）\n\n\u003e 概念：外部与一个子系统的通信必须通过一个统一的外观对象进行，为子系统中的一组接口提供一个一致的界面，外观模式定义了一个高层接口，这个接口使得这一子系统更加容易使用！\n\u003e\n\u003e 目的：使得系统代码更加灵活，松耦合，易移植\n\n**门面基类（Facade.php）**\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App\\Facade;\n\nuse App\\Container;\n\nclass Facade\n{\n    /**\n     * 获取当前Facade对应类名,由子类实现\n     * @access protected\n     * @return string\n     */\n    protected static function getFacadeClass() {}\n\n    // 调用实际类的方法\n    public static function __callStatic($method, $params)\n    {\n      \t# 结合容器，获取实例对象\n        $class = Container::getInstance()-\u003emake(static::getFacadeClass());\n        return call_user_func_array([$class, $method], $params);\n    }\n}\n```\n\n**实现一个缓存案例**\n\n\u003e 1、使用 cache 别名，将 Cache 类注册进容器内；\n\u003e\n\u003e 2、Library 下新建 Cache\\Redis 与 Cache\\File 两个类。后面可以通过 Provider 进行切换\n\u003e\n\u003e 3、Library 下新建 Psr\\CacheInterface 接口，约束 Cache\\Redis 与 Cache\\File\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App\\Facade;\n\n/**\n * @see \\App\\Facade\\Cache\n * @package App\\Facade\n * @mixin \\App\\Facade\\Cache\n * @method static string set($key, $value) 设置缓存\n */\nclass Cache extends Facade\n{\n    /**\n     * 获取当前Facade对应类名（或者已经绑定的容器对象标识）\n     * @access protected\n     * @return string\n     */\n    protected static function getFacadeClass()\n    {\n        return 'cache';\n    }\n}\n```\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App\\Library\\Cache;\n\nuse Psr\\SimpleCache\\CacheInterface;\n\nclass Redis implements CacheInterface\n{\n    public function set($key, $value, $ttl = null) {\n        return \"RedisCache: key: {$key}, value: {$value}\";\n    }\n\n    public function get($key, $default = null)\n    {}\n\n    public function delete($key)\n    {}\n\n    public function clear()\n    {}\n\n    public function has($key)\n    {}\n}\n```\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App\\Library\\Cache;\n\nuse Psr\\SimpleCache\\CacheInterface;\n\nclass File implements CacheInterface\n{\n    public function set($key, $value, $ttl = null) {\n        return \"FileCache: key: {$key}, value: {$value}\";\n    }\n\n    public function get($key, $default = null)\n    {}\n\n    public function delete($key)\n    {}\n\n    public function clear()\n    {}\n\n    public function has($key)\n    {}\n}\n```\n\n```php\n\u003c?php\n\nnamespace Psr\\SimpleCache;\n\ninterface CacheInterface\n{\n    public function get($key, $default = null);\n\n    public function set($key, $value, $ttl = null);\n\n    public function delete($key);\n\n    public function clear();\n\n    public function has($key);\n}\n\n```\n\n---\n\n### 参考与借鉴\n\n- [简书 - 搞懂依赖注入, 用 PHP 手写简易 IOC 容器](https://www.jianshu.com/p/844ae48975f4)\n- [Thinkphp6.0 - 优秀的 PHP 框架](https://github.com/top-think/framework)\n- [Laravel5.x - 优雅的 PHP 框架](https://github.com/laravel/framework)\n\n### 接下来（加油...）\n\n\u003e 可以自己动手，结合IOC容器 + 依赖注入 + 外观模式实现自己的小框架，更加深入的了解现代 php 编码的设计模式！！\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flmsail%2Fphp-ioc-facade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flmsail%2Fphp-ioc-facade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flmsail%2Fphp-ioc-facade/lists"}