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

PHP依赖注入与服务容器深度剖析

PHP依赖注入与服务容器深度剖析

依赖注入是控制反转的一种实现方式。框架中的服务容器负责管理对象的创建和依赖关系。今天深入说说服务容器的实现原理。

先回顾一下依赖注入的基本概念。传统的代码在类内部创建依赖,导致紧耦合。依赖注入把依赖从外部传入,实现松耦合。

```php
// 没有依赖注入
class OrderProcessor
{
private Logger $logger;

public function __construct()
{
// 在内部创建依赖,紧耦合
$this->logger = new FileLogger('/var/log/app.log');
}
}

// 构造函数注入
class OrderProcessorDI
{
public function __construct(
private LoggerInterface $logger // 依赖从外部传入
) {}
}

// 属性注入
class OrderProcessorProperty
{
public LoggerInterface $logger; // 从外部设置
}

// Setter注入
class OrderProcessorSetter
{
private LoggerInterface $logger;

public function setLogger(LoggerInterface $logger): void
{
$this->logger = $logger;
}
}
?>
```

自动装配是容器的核心功能。通过反射分析类的构造函数,递归解析所有依赖。

```php
class Container
{
private array $bindings = [];
private array $instances = [];
private array $aliases = [];

public function bind(string $abstract, callable|string|null $concrete = null, bool $shared = false): void
{
if ($concrete === null) {
$concrete = $abstract;
}

if (! $concrete instanceof Closure) {
$concrete = function (Container $container) use ($concrete) {
return $container->build($concrete);
};
}

$this->bindings[$abstract] = compact('concrete', 'shared');
}

public function singleton(string $abstract, callable|string|null $concrete = null): void
{
$this->bind($abstract, $concrete, true);
}

public function instance(string $abstract, object $instance): void
{
$this->instances[$abstract] = $instance;
}

public function alias(string $abstract, string $alias): void
{
$this->aliases[$alias] = $abstract;
}

public function make(string $abstract): mixed
{
$abstract = $this->resolveAlias($abstract);

if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}

if (!isset($this->bindings[$abstract])) {
return $this->build($abstract);
}

$binding = $this->bindings[$abstract];
$object = ($binding['concrete'])($this);

if ($binding['shared']) {
$this->instances[$abstract] = $object;
}

return $object;
}

public function call(callable|array $callable, array $parameters = []): mixed
{
if (is_array($callable)) {
$ref = new ReflectionMethod($callable[0], $callable[1]);
} else {
$ref = new ReflectionFunction($callable);
}

$args = [];
foreach ($ref->getParameters() as $param) {
$name = $param->getName();

if (isset($parameters[$name])) {
$args[] = $parameters[$name];
continue;
}

$type = $param->getType();
if ($type instanceof ReflectionNamedType && !$type->isBuiltin()) {
$args[] = $this->make($type->getName());
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析参数: $name");
}
}

return $ref->invokeArgs($args);
}

public function build(string $class): object
{
if (!class_exists($class)) {
throw new RuntimeException("无法构建: $class");
}

$ref = new ReflectionClass($class);
$constructor = $ref->getConstructor();

if ($constructor === null) {
return $ref->newInstance();
}

$dependencies = [];
foreach ($constructor->getParameters() as $param) {
$type = $param->getType();

if ($type === null) {
if ($param->isDefaultValueAvailable()) {
$dependencies[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析参数: {$param->getName()}");
}
} elseif ($type->isBuiltin()) {
if ($param->isDefaultValueAvailable()) {
$dependencies[] = $param->getDefaultValue();
} else {
throw new RuntimeException("无法解析内置类型: {$param->getName()}");
}
} else {
$dependencies[] = $this->make($type->getName());
}
}

return $ref->newInstanceArgs($dependencies);
}

public function has(string $abstract): bool
{
$abstract = $this->resolveAlias($abstract);
return isset($this->bindings[$abstract])
|| isset($this->instances[$abstract])
|| class_exists($abstract);
}

private function resolveAlias(string $abstract): string
{
return $this->aliases[$abstract] ?? $abstract;
}
}
?>
```

服务提供者模式把相关的绑定和初始化逻辑组织在一起,是框架扩展的标准方式。

```php
interface ServiceProvider
{
public function register(Container $container): void;
public function boot(Container $container): void;
}

class CacheServiceProvider implements ServiceProvider
{
public function register(Container $container): void
{
$container->singleton('cache.redis', function () {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis;
});

$container->bind('cache.file', function () {
return new FileCache('/tmp/cache');
});
}

public function boot(Container $container): void
{
$redis = $container->make('cache.redis');
$redis->ping();
}
}

class App
{
private Container $container;
private array $providers = [];

public function __construct()
{
$this->container = new Container();
}

public function register(array $providers): void
{
foreach ($providers as $provider) {
$instance = new $provider();
$instance->register($this->container);
$this->providers[] = $instance;
}
}

public function boot(): void
{
foreach ($this->providers as $provider) {
$provider->boot($this->container);
}
}

public function make(string $abstract): mixed
{
return $this->container->make($abstract);
}
}

$app = new App();
$app->register([CacheServiceProvider::class]);
$app->boot();

$redis = $app->make('cache.redis');
echo "应用启动完成\n";
?>
```

依赖注入和服务容器是框架的核心基础设施。理解它们的实现原理,可以更高效地使用框架,在遇到问题时也能快速定位。不管是自己实现容器还是使用框架自带的容器,核心思想都是一样的。

http://www.rkmt.cn/news/1438355.html

相关文章:

  • Flink 1.17 监控实战:5分钟搞定JMX和Slf4j日志双指标上报
  • 别再让SSD‘偏科’了!聊聊主控芯片里的‘雨露均沾’算法:动态与静态磨损均衡到底怎么选?
  • 手把手教你为旧版Linux系统(如Xubuntu 16.04)打RT补丁并编译内核
  • 别再只盯着Stegsolve了!聊聊CTF图片隐写中那些‘非主流’工具:从foremost分离到outguess解密实战
  • 告别Putty:用Windows Terminal或VSCode远程SSH连接树莓派,体验更现代的终端操作
  • 用AVR单片机解码DALI信号:一个定时器+GPIO中断的实战拆解(附Microchip参考代码)
  • FreeRTOS任务栈分配踩坑记:为什么我的LVGL任务跑着跑着就卡住了?
  • 避开Gazebo仿真坑:手把手教你配置Livox非重复扫描雷达的URDF模型
  • 抖音素材收集革命:5分钟搞定无水印批量下载,自媒体人必备神器!
  • Spring Boot项目引入自家SDK JAR包踩坑记:从恼人的打包警告到优雅的依赖管理方案
  • PHP依赖注入容器原理与实现
  • AI如何重塑蓝领工作:从自动化到人机协作的转型路径
  • 别再死记硬背74LS138真值表了!用这个实验箱实战一次,彻底搞懂3-8译码器
  • SwanLab离线版远程访问全攻略:从单机到团队协作,安全共享你的实验看板
  • 别再为IP核仿真头疼了!手把手教你用Vivado 2018.3给ModelSim 22.04编译专属仿真库
  • 混沌系统随机性好不好?手把手教你用NIST测试包和Matlab出报告
  • 别再死记硬背了!通过一个校园网项目,彻底搞懂VLAN、VRRP和OSPF是怎么协同工作的
  • 别再只盯着CTR了!硬件工程师必看:光耦选型时这5个参数才是关键(附避坑指南)
  • SQL开发者如何通过特征工程与数据库内机器学习实现技能升级
  • 量子计算与无网格粒子法融合:Q-FPM框架解析
  • AI 智能体总是跑偏怎么办?ChatGPT/API/Agent 故障排查指南与全流程修复手册
  • 代工厂和贴牌品牌方在数据上怎么分?
  • 用Python+OpenCV给视频藏个秘密:手把手教你实现CTF风格的帧隐写(附完整代码)
  • OPC中国正在重新定义大学生的第一份工作
  • 保姆级教程:用tippecanoe+Mapbox GL JS,5步搞定OSM数据矢量瓦片可视化
  • SpikingBrain模型:脉冲编码与INT8量化联合优化实践
  • 别再只画直线了!HFSS里微带线弯折、切角与阻抗匹配的那些“潜规则”与实战技巧
  • SwanLab离线版远程访问保姆级教程:从云服务器到本地Mac/Windows的完整配置流程
  • 用STM32L152+FPGA打造高精度万用表?这份开源项目的避坑指南与实战配置
  • PHPAPI网关实现与请求路由