news 2026/9/19 2:35:59

如何从零手写一个DI容器:基于gh_mirrors/co/container实现PSR-11合规容器的完整教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何从零手写一个DI容器:基于gh_mirrors/co/container实现PSR-11合规容器的完整教程

如何从零手写一个DI容器:基于gh_mirrors/co/container实现PSR-11合规容器的完整教程

【免费下载链接】container项目地址: https://gitcode.com/gh_mirrors/co/container

本教程基于 gh_mirrors/co/container(PSR-11 标准接口仓库),带你从零手写一个 PHP 依赖注入容器(DI 容器),掌握 PSR-11 合规容器的全部 3 个接口与 2 个核心方法。📦

💡 为什么值得学?DI 容器是现代 PHP 框架(如 Laravel、Symfony)的基石。读懂 PSR-11 这 3 个几十行的接口文件,你就掌握了所有合规容器的"公共语言"。

什么是DI容器?1分钟看懂核心思想

DI 容器(Dependency Injection Container,依赖注入容器)本质是一个"服务工厂":

  • 传统写法:类 A 内部自己new类 B,两者强耦合,难以测试和替换;
  • DI 容器写法:把"创建对象"的职责交给容器,类 A 只声明"我需要 B",由容器统一组装和交付。

好处一目了然:✅ 依赖集中管理、✅ 便于单元测试(可注入 Mock 对象)、✅ 替换实现时业务代码零改动。

PSR-11是 PHP-FIG 制定的"容器接口标准"——它规定任何容器都至少暴露统一的get()has()方法。gh_mirrors/co/container 正是这份标准的接口定义仓库,只有 3 个接口文件、没有一行实现代码,这正是我们"从零手写"的完美起点。

PSR-11核心剖析:3个接口文件定乾坤

仓库结构极其精简,src/目录下仅 3 个文件:

文件作用一句话理解
ContainerInterface.php容器主体接口容器的"脸面":get()+has()
ContainerExceptionInterface.php容器异常基接口容器出错的"统一身份证"
NotFoundExceptionInterface.php"找不到"异常接口专门标记"查无此服务"

第一步:读懂 ContainerInterface 的两个方法

整个 PSR-11 标准的核心就在这两个方法声明里:

  • get(string $id):按标识符取对象,是容器的"主入口";
  • has(string $id): bool:判断容器是否认识某个标识符。

其中有一段非常关键的官方约定(src/ContainerInterface.php):

has($id)返回 true不保证get($id)一定不抛异常,但保证不会抛"未找到异常"。

换句话说:has()管"有没有这个 key",get()还负责"构造这个对象"——构造过程中仍可能因其他原因失败。写实现时务必遵守这条语义。

第二步:理解两级异常体系

异常接口只有寥寥数行,却设计了清晰的继承链:

Throwable └── ContainerExceptionInterface(容器通用异常) └── NotFoundExceptionInterface(未找到异常)
  • 基接口定义在 ContainerExceptionInterface.php,要求"所有容器异常都继承它";
  • 未找到接口定义在 NotFoundExceptionInterface.php,专用于"key 不存在"场景。

这样设计的好处:调用方可以分级捕获——catch NotFoundExceptionInterface只处理"没找到",catch ContainerExceptionInterface兜底其他构建失败。错误语义从此清晰可控。🎯

3步写出你的PSR-11合规容器

一键获取标准接口包

标准库通过 Composer 包psr/container分发(元数据见 composer.json,要求 PHP >= 7.4,命名空间Psr\Container映射到src/目录)。获取仓库:

git clone https://gitcode.com/gh_mirrors/co/container composer require psr/container

实现两个方法:最简容器诞生

手写实现其实非常短——一个数组存服务、两个方法做存取即可:

<?php use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; final class MyContainer implements ContainerInterface { private array $services = []; public function set(string $id, $service): void { $this->services[$id] = $service; } public function get(string $id) { if (!array_key_exists($id, $this->services)) { throw new class("Service [$id] not found.") extends \RuntimeException implements NotFoundExceptionInterface {}; } return $this->services[$id]; } public function has(string $id): bool { return array_key_exists($id, $this->services); } }

对照标准检查一遍:get()未找到时抛出的异常实现了 NotFoundExceptionInterface ✅;has()返回 bool ✅。两个方法齐全,恭喜——你的容器已通过 PSR-11 合规检验

注册与获取:3行代码感受依赖注入

$container = new MyContainer(); $container->set('logger', new MonologLogger()); $logger = $container->get('logger'); // 从容器取,而非 new

业务类只需声明__construct(Logger $logger),由容器注入实例——依赖倒置就此完成。🚀

常见踩坑与最佳实践清单

  • ⚠️key 命名要稳定$id是全局契约,建议用"类型名或语义化名称",如loggerapp.config
  • ⚠️区分"没找到"和"构造失败":前者抛NotFoundExceptionInterface,后者抛ContainerExceptionInterface的其它实现,别混为一谈;
  • ⚠️has()为 true 不代表get()无异常:这是官方明文约定(见 src/ContainerInterface.php),不要据此省略异常处理;
  • 面向接口编程:业务代码依赖ContainerInterface而非你的MyContainer,未来无缝切换任何 PSR-11 容器;
  • 进阶方向:把set()升级为"工厂闭包"(传入Closure延迟实例化),即可实现自动依赖解析,这也是各大框架容器的核心机制。

总结与延伸资源

回顾一下这条"从零到合规"的路线:

  1. 理解标准——PSR-11 只用 3 个接口定义容器的行为契约;
  2. 实现get()/has()——两行判断、一个数组,最简容器即可成立;
  3. 规范异常——用两级异常体系表达"没找到"与"构建失败"。

📁 核心资料速查:

  • 容器主体接口:ContainerInterface.php
  • 异常基接口:ContainerExceptionInterface.php
  • 未找到异常接口:NotFoundExceptionInterface.php
  • 包元数据与自动加载配置:composer.json
  • 项目说明:README.md

标准只有薄薄一层,真正让 DI 容器变强大的,是你的工厂闭包、自动解析和缓存策略。动手把上面的MyContainer扩展成支持工厂的版本吧——你会离手写一个"迷你版框架容器"只差一小步!✨

【免费下载链接】container项目地址: https://gitcode.com/gh_mirrors/co/container

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 2:34:26

Obsidian 加 Git 搭建本地知识库:双向链接与版本控制实战

1. 为什么我最终选择了 Obsidian 加 Git 这套组合1.1 从笔记越写越乱说起我用过的笔记软件不算少&#xff0c;从最早的印象笔记&#xff0c;到后来的语雀、Notion&#xff0c;再到本地优先的思源笔记&#xff0c;几乎每一款都深度用过至少三个月。但真正让我停下来、决定长期投…

作者头像 李华
网站建设 2026/9/19 2:33:52

基于DeepSeek与敏感词检测的银行理财合规话术自动生成方案

简介&#xff1a;这份文档围绕DeepSeek在银行理财合规话术生成中的应用&#xff0c;面向金融科技从业者与AI算法工程师&#xff0c;提供从敏感词实时检测到合规文本自动重构的完整技术方案。资源为1个PDF文件&#xff0c;压缩包大小14.37MB&#xff0c;共471页、51个大章节&…

作者头像 李华
网站建设 2026/9/19 2:31:16

HarmonyOS如何开发星闪SLE智能家居控制应用?从原理到实战全解析

做智能家居这些年&#xff0c;我一直在关注短距无线通信方案的演进。蓝牙功耗低但时延不稳定&#xff0c;WiFi带宽够但费电&#xff0c;Zigbee组网强但速率太低。所以当星闪SLE出现在公开技术资料里的时候&#xff0c;我就觉得这个方向值得提前押注——低时延、高并发、低功耗&…

作者头像 李华
网站建设 2026/9/19 2:31:07

天地图市级节点多源地理数据聚合:从HTML解析到空间服务发布

简介&#xff1a;一份围绕“天地图常州”的地理数据解析与聚合方法研究PDF&#xff0c;聚焦大数据算法在地理信息公共服务平台中的应用&#xff0c;适合地理信息、数据挖掘及智慧城市方向的研究者、平台开发者和相关专业学生。该研究针对“天地图”基础测绘数据难以满足公众服务…

作者头像 李华
网站建设 2026/9/19 2:31:05

RSMA安全传输:预编码优化与NOMA/SDMA对比仿真

简介&#xff1a;面向无线通信安全研究的论文复现资料&#xff0c;围绕速率分割多址接入&#xff08;RSMA&#xff09;的安全传输预编码优化展开系统阐述。内容将RSMA与NOMA、SDMA统一于下行广播模型中&#xff0c;详细介绍用户消息拆分、公共流与私有流预编码设计、连续干扰消…

作者头像 李华