ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

ShowDoc 后端中的 PHP-DI/Invoker:通用可扩展的 PHP 可调用对象调用器实战指南

ShowDoc 后端中的 PHP-DI/Invoker:通用可扩展的 PHP 可调用对象调用器实战指南 ShowDoc 后端中的 PHP-DI/Invoker通用可扩展的 PHP 可调用对象调用器实战指南【免费下载链接】showdocShowDoc is a tool greatly applicable for an IT team to share documents online一个非常适合IT团队的在线API文档、技术文档工具项目地址: https://gitcode.com/gh_mirrors/sh/showdoc输出文章ShowDoc 后端中的 PHP-DI/Invoker通用可扩展的 PHP 可调用对象调用器实战指南导读本篇文章围绕 php-di/invoker 的官方 README 展开系统讲解这一通用、可扩展的 PHP callable可调用对象调用器从它诞生的动机按参数名调用、依赖注入扩展点、安装方式、默认调用行为到参数解析器ParameterResolver体系与容器解析 callable 的完整能力。ShowDoc 的后端基于 ThinkPHP 3.2 自研 app 分层server/app并在 server/vendor 中通过 Composer 引入了该库理解 Invoker 的调用链对阅读、扩展 ShowDoc 后端代码尤其是各类 Controller 分发的实现有直接帮助。读完本文你将掌握如何在任何 PSR-11 容器之上用 Invoker 实现“按名传参 自动注入”的统一调用层。为什么需要 Invoker从call_user_func的痛点说起PHP 原生的call_user_func/call_user_func_array只能按照参数的位置传参而真实业务中控制器、命令、事件处理器往往期望更灵活的调用方式。README 给出了三类典型场景按名字匹配参数Silex 路由 / Silly 命令 / Slim 路由// Silex 风格无论参数顺序如何按参数名匹配 $app-get(/project/{project}/issue/{issue}, function ($project, $issue) { // ... }); // Silly 命令风格 $app-command(greet [name] [--yell], function ($name, $yell) { // ... }); // Slim 风格 $app-get(/hello/:name, function ($name) { // ... });依赖注入AngularJS 中通过数组字符串声明依赖注入控制器PHP 生态中 Silex 只支持Silex\Application一个类型注入Silly 只支持OutputInterface而 PHP-DI 容器可以基于类型提示解析任意依赖$container-call(function (Logger $logger, EntityManager $em) { // ... });Invoker 的定位正如其 README 标题所写Generic and extensible callable invoker它是一个“基座构件”base building block让框架可以在其上实现任何想要的命名参数与依赖注入支持。安装与依赖安装方式为 Composercomposer require PHP-DI/invoker从仓库内的 composer.json 可以看到其硬性依赖非常轻量PHP 7.3psr/container^1.0 || ^2.0即只需要 PSR-11 容器接口无需绑定任何具体容器实现。这意味着无论你在项目中使用 PHP-DI、自定义容器还是任意 PSR-11 兼容实现都可以直接接入 Invoker。在 ShowDoc 仓库中该库位于 server/vendor/php-di/invoker作为 vendor 依赖被引入。默认行为开箱即用的调用器Invoker\Invoker默认即可完成命名参数调用。以下示例全部来自 README且与源码中默认参数解析器链一一对应$invoker new Invoker\Invoker; // 无参调用 $invoker-call(function () { echo Hello world!; }); // 简单的位置参数数组 $invoker-call(function ($name) { echo Hello . $name; }, [John]); // 按参数名传参关联数组 $invoker-call(function ($name) { echo Hello . $name; }, [ name John ]); // 使用默认值 $invoker-call(function ($name world) { echo Hello . $name; }); // 调用任意 PHP callable数组形式 $invoker-call([MyClass, myStaticMethod]); // 调用任意 PHP callableClass::method 字符串形式 $invoker-call(MyClass::myStaticMethod);默认参数解析器链的源码细节Invoker::call()的核心流程在 Invoker.php 中先做 callable 合法性校验不合法抛出NotCallableException再通过CallableReflection::create()生成反射交给参数解析器解析实参最后用call_user_func_array执行执行前ksort保证按参数位置排序。默认解析器链在createParameterResolver()中构造顺序为NumericArrayResolver处理纯位置数组[John]AssociativeArrayResolver按参数名匹配关联数组源码中通过array_key_exists($parameter-name, $providedParameters)完成“按名注入”DefaultValueResolver参数缺失时使用反射获取的默认值如$name world。三个解析器被包装在 ResolverChain.php 中该链采用责任链模式逐个调用解析器一旦array_diff_key($reflectionParameters, $resolvedParameters)为空即所有参数都已解析立即停止遍历。当某个参数最终没有被任何解析器解析且不是变长参数时Invoker::call()会抛出NotEnoughParametersExceptionUnable to invoke the callable because no value was given for parameter %d ($%s)参数解析器Invoker 的可扩展核心ParameterResolver 接口与 ResolverChain扩展 Invoker 行为的方式是自行实现ParameterResolver接口源码位于 ParameterResolver.php并通过ResolverChain注册// 追加到链尾 $invoker-getParameterResolver()-appendResolver($resolver); // 插入到链首优先执行README 推荐用于容器注入 $invoker-getParameterResolver()-prependResolver($containerResolver);ResolverChain源码是责任链的实现载体每个解析器都会收到“反射、用户提供的参数、已解析出的参数”三份输入返回解析结果链在全部参数解析完成后提前终止。内置的两个 DI 容器解析器仓库随包附带两个可选解析器均面向 PSR-11 容器1.TypeHintContainerResolver按类型提示注入$invoker-call(function (Psr\Logger\LoggerInterface $logger) { // ... });此例中 Invoker 会执行$container-get(Psr\Logger\LoggerInterface)并注入。查看 TypeHintContainerResolver.php 源码其解析逻辑为遍历尚未解析的参数要求其类型为ReflectionNamedType且非内置类型isBuiltin()为 false 时才处理因此标量类型会被跳过self会解析为声明类名最后仅在$container-has($parameterClass)为真时才取值注入。使用前提容器必须以类名/接口名作为条目 key 存储对象。2.ParameterNameContainerResolver按参数名注入$invoker-call(function ($twig) { // ... });此例中会执行$container-get(twig)并注入。源码逻辑更简单只要$container-has($parameter-name)为真就注入。使用前提容器以服务名如twig、db作为条目 key典型如 Silex、Symfony 的自定义命名服务。两个解析器都只依赖Psr\Container\ContainerInterface可与任何 PSR-11 容器协作。组装示例// $container 必须是 Psr\Container\ContainerInterface 实例 $container ...; $containerResolver new TypeHintContainerResolver($container); // 或 $containerResolver new ParameterNameContainerResolver($container); $invoker new Invoker\Invoker; // 务必在其它参数解析器之前注册 $invoker-getParameterResolver()-prependResolver($containerResolver);两个解析器可以同时 prepend 注册顺序即优先级更复杂的注入规则如属性注解、工厂闭包也可以自行实现ParameterResolver接入。从容器解析 callable框架分发器的基座除了参数注入Invoker 还支持将 callable 本身交给容器解析——这是实现框架路由分发器的关键能力。可调用类invokable classclass MyHandler { public function __invoke() { // ... } } // 默认不工作需要手动传入实例 $invoker-call(MyHandler); // 传入容器后即可工作 $invoker new Invoker\Invoker(null, $container); $invoker-call(MyHandler); // MyHandler 会由容器解析为实例类方法Controller 风格class WelcomeController { public function home() { // ... } } // 默认不工作home() 不是静态方法 $invoker-call([WelcomeController, home]); // 传入容器后即可工作 $invoker new Invoker\Invoker(null, $container); $invoker-call([WelcomeController, home]); // 也支持 Class::method 字符串语法 $invoker-call(WelcomeController::home);CallableResolver 的解析机制该能力由 CallableResolver.php 实现只有当构造函数传入容器时才会创建。其解析顺序为字符串形式的Class::method先按::拆分为[Class, method]Closure直接短路返回最常见的高频场景已可直接调用的 callable 直接返回——但会额外检查“以静态方式调用非静态方法”的情况通过反射ReflectionMethod::isStatic()判断若非静态则继续走容器解析字符串形式的容器条目名尝试$container-get($callable)捕获NotFoundExceptionInterface后按容器has()结果决定抛NotCallableException还是原样抛出数组形式且首元素是字符串把首元素替换为$container-get($callable[0])得到的实例从而支持[some-container-entry, methodToCall]这种“容器条目 方法名”的调用写法。整套机制只依赖Psr\Container\ContainerInterfaceREADME 明确说明任何 PSR-11 兼容容器都可以直接提供。在 ShowDoc 后端中的定位与阅读指引从仓库结构看php-di/invoker 位于 server/vendor/php-di/invoker是 ShowDoc 后端ThinkPHP 3.2 体系 server/app 自研分层的 Composer 依赖之一。虽然该库在本仓库中属于 vendor 依赖不直接改动但理解它的调用模型有助于阅读 server/app/Api/Controller 下各类 Controller 的分发与参数处理逻辑理解“按名匹配参数”在框架分发中的意义在基于 PSR-11 容器定制 ShowDoc 服务调用时直接复用 Invoker 的解析器链设计作为参考实现借鉴其“责任链 可插拔解析器”的架构风格来扩展自己的调用层。仓库根目录的 composer.json 与 server/vendor/php-di/invoker/composer.json 记录了依赖版本与自动加载PSR-4Invoker\→src/关系可据此进一步确认实际安装版本与扩展方式。小结PHP-DI/Invoker 通过“参数解析器责任链 容器 callable 解析器”两个正交的扩展点把 PHP 中“按名传参调用”与“依赖自动注入”这两个高频需求统一成了一个几十 KB 的轻量构件。其核心要点可以归纳为能力实现载体关键限制位置参数调用NumericArrayResolver按数组下标对应参数位置命名参数调用AssociativeArrayResolver关联数组 key 需与参数名一致参数默认值DefaultValueResolver依赖反射获取默认值按类型提示注入TypeHintContainerResolver仅支持命名类型、非内置类型容器需以类名存储按参数名注入ParameterNameContainerResolver容器需以服务名存储callable 容器解析CallableResolver需在构造 Invoker 时传入 PSR-11 容器对于希望在框架层实现统一分发器、或在现有 PSR-11 容器基础上获得“按名调用 自动注入”能力的开发者而言php-di/invoker 是一个值得直接复用的基础构建块。【免费下载链接】showdocShowDoc is a tool greatly applicable for an IT team to share documents online一个非常适合IT团队的在线API文档、技术文档工具项目地址: https://gitcode.com/gh_mirrors/sh/showdoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表