Hyperf 3.1 落地 PSR 规范需严格遵循契约:PSR-3 要求实现 LoggerInterface 并委托 8 个级别方法至 log(),否则违反接口一致性;PSR-4 必须保证命名空间与目录路径严格对应且 composer dump-autoload 刷新映射,否则类加载失败;PSR-7 须通过类型提示自动转换 Swoole 请求为 ServerRequestInterface,避免协程下超全局变量污染;PSR-11 推荐构造函数注入,属性注入需 public/protected + #[Inject],手动 get() 仅用于动态场景,否则破坏解耦与可测性。

面试官问你Hyperf 3.1中如何落地PSR规范,不是让你背定义,而是看你能不能在真实项目里写出符合PSR-3、PSR-4、PSR-7、PSR-11的可运行代码,且能解释清楚为什么必须这么写、不这么写会出什么问题。
PSR-3日志接口:手写FileLogger并注入容器
Hyperf默认已集成PSR-3兼容的日志器,但面试常考“自己实现一个并注册到DI容器”,重点考察接口契约理解与依赖注入实操。
第一步:创建app/Logger/FileLogger.php,严格实现PsrLogLoggerInterface,注意必须用use PsrLogLoggerInterface引入官方接口,不能自定义同名接口。
第二步:在构造函数中声明private string $logFile,并赋默认值'app.log'——【若未设默认值且调用时未传参,容器注入会直接报错】。
第三步:重写log()方法,用date('Y-m-d H:i:s')拼时间戳,再调用file_put_contents($this->logFile, $entry, FILE_APPEND)追加写入。其他8个级别方法(info()、error()等)全部委托给log(),不可重复实现逻辑。
第四步:在config/autoload/dependencies.php中绑定:LoggerInterface::class => FileLogger::class。这一步必须做,否则__construct(private LoggerInterface $logger)无法解析。
PSR-4自动加载:目录结构与composer.json双校验
Hyperf项目必须满足PSR-4规范才能让类自动加载生效,面试官会检查你是否真懂“命名空间 = 目录路径”这一硬约束。
方法一:确认composer.json中"autoload": {"psr-4": {"App\": "app/"}}存在且无拼写错误。漏掉末尾斜杠或写成app(无斜杠)会导致AppControllerUserController找不到文件。
方法二:检查app/Controller/UserController.php顶部声明namespace AppController;,且文件物理路径必须严格匹配——【若把文件放在app/controller/UserController.php(小写controller),即使命名空间写对,也会加载失败】。
方法三:执行composer dump-autoload -o强制刷新自动加载映射。开发中改了命名空间或移动了文件,不执行这步,旧缓存会让新路径失效。
PSR-7 HTTP消息:用SwooleRequest转标准Request对象
Hyperf底层基于Swoole协程服务器,原始请求是SwooleHttpRequest,但业务层必须用PSR-7标准PsrHttpMessageServerRequestInterface,否则中间件、验证器等组件无法工作。
第一步:在控制器方法参数中直接类型提示ServerRequestInterface $request,Hyperf会自动完成转换,无需手动new。
第二步:获取GET参数用$request->getQueryParams(),POST表单用$request->getParsedBody(),原始JSON用$request->getBody()->getContents()——【若误用$_GET或$_POST,在协程环境下可能读到其他请求的数据】。
第三步:构造响应时,必须返回PsrHttpMessageResponseInterface实例,推荐用$this->response->json([...]),它内部已封装好PSR-7响应对象。
PSR-11容器:三种获取服务的方式及适用场景
面试必问“什么时候该用$container->get(),什么时候该用构造函数注入”,本质是考你对容器生命周期和解耦原则的理解。
① 构造函数注入(推荐):在服务类__construct()中声明类型提示,如public function __construct(private RedisInterface $redis)。容器启动时自动解析注入,性能高、可测试性强。
② 属性注入(Hyperf 3.1新增):在属性上加#[Inject],如#[Inject] protected HttpClientInterface $http;。适合可选依赖,避免构造函数参数爆炸,但【属性不能是private,否则注入失败】。
③ 手动获取(慎用):在方法内调用$container->get(RedisInterface::class)。仅限动态服务名场景(如根据配置决定用Redis还是Memcached),否则破坏依赖倒置,导致单元测试难mock。


















