webman容器必须返回psr-11兼容实例,错误配置会导致启动报错;需用di\containerbuilder构建并启用autowiring与attributes;中间件中应提前用container::make($class, [$request])避免控制器重复创建和request丢失。

Webman 的 Container 不是“用不用”的问题,而是“怎么用才不踩坑”的问题。它默认启用 PHP-DI,但配置方式、实例生命周期、参数传递时机稍有偏差,就会导致控制器重复构造、Request 对象丢失、测试时无法 mock 依赖——这些不是框架 bug,是容器行为没对齐预期。
config/container.php 返回的必须是 PSR-11 兼容实例
你不能直接 return new Webman\Container() 就完事。Webman 6+ 默认使用 php-di/php-di,其容器构建器返回的是 DI\Container 实例,它实现了 Psr\Container\ContainerInterface。如果你手写一个裸容器(比如只含 get() 和 set() 的简单类),但没实现该接口,启动时会报错:Argument 1 passed to Webman\App::__construct() must implement interface Psr\Container\ContainerInterface。
正确做法是沿用官方推荐的 builder 模式:
use DI\ContainerBuilder;
$builder = new ContainerBuilder();
$builder->addDefinitions(config('dependence', []));
$builder->useAutowiring(true);
$builder->useAttributes(true);
return $builder->build();
注意三点:
-
addDefinitions()加载的是config/dependence.php中定义的服务映射,不是container.php本身 -
useAutowiring(true)启用自动构造解析,否则带类型提示的构造函数参数不会被自动注入 -
useAttributes(true)支持 PHP 8.0+ 的#[Inject]属性注入,但仅限于属性,不触发构造函数
Controller 实例在中间件里被重复创建?改 Container::get() 不如改调用时机
Webman 默认在中间件中调用 Container::get($controllerClass),而这个方法每次都会 new 一个新实例——导致 __construct(Request $request) 里的 $request 在中间件和 action 中不是同一个对象,$this->xxx 状态也无法共享。
这不是容器配置问题,是调用链路设计问题。解决思路不是重写 get(),而是提前用 make() 注入 $request 并缓存:
- 在
app/App.php的getCallback()方法中,路由匹配后、中间件执行前,调用Container::make($controllerClass, [$request]) - 确保
make()的第二个参数是数组,且顺序与构造函数参数严格一致(PHP-DI 不支持按名传参) - 避免在中间件里再调
Container::get(),改用已传入的 controller 实例或从 request 属性中取上下文
验证方式:打印 spl_object_hash($this),同一请求下中间件和 action 中的 controller 实例 hash 值应完全相同。
手动绑定服务时,bind() 和 set() 行为差异极大
Webman 容器底层是 PHP-DI,它没有 bind() 方法。如果你看到别人代码里写了 Container::bind('logger', ...),那大概率是混用了其他容器(比如 Laravel 或自研轻量版)。Webman 原生只提供 get()、has()、make(),以及通过 config/dependence.php 声明定义。
要在运行时动态注册服务,必须走 PHP-DI 的定义数组格式:
// config/dependence.php
return [
'logger' => \DI\create(\Monolog\Logger::class)
->constructor('webman')
->method('pushHandler', \DI\create(\Monolog\Handler\StreamHandler::class)
->constructor(__LOG_PATH__ . '/app.log')),
];
关键点:
- 键名(如
'logger')是服务标识符,后续用Container::get('logger')获取 -
\DI\create()是工厂函数,不是 new;constructor()和method()是链式调用,用于指定构造参数和方法调用 - 不要在
dependence.php里写业务逻辑(比如读配置、连 DB),只做声明;实际初始化延迟到第一次get()时
最易被忽略的点:PHP-DI 的 make() 不会把结果存进单例池,除非你在定义里显式声明 ->singleton() 或在 dependence.php 中用 \DI\factory() + 手动缓存。这意味着,哪怕你连续调用 10 次 Container::make(UserService::class),也会得到 10 个不同实例——除非你在定义中明确标记为单例。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











