开发工具静态分析【免费下载链接】TypeResolverA PSR-5 based resolver of Class names, Types and Structural Element Names项目地址https://gitcode.com/gh_mirrors/ty/TypeResolver点击查看免费下载本文是一份面向 PHP 开发者的 TypeResolver 上手指南。phpDocumentor 的 TypeResolver 是一个基于 PSR-5 规范实现的类型解析库负责把 DocBlock 中的类型表达式如string、string|integer、Types\Context以及结构元素名称如Classy::otherFunction()解析为对应的值对象Value Object。读完本文你将掌握 TypeResolver 的安装方式、TypeResolver与FqsenResolver两大解析器的基本用法、如何借助Context将部分类名展开为完全限定类名FQCN以及底层解析流程的实现原理。一、安装与项目环境TypeResolver 已发布到 Packagist可通过 Composer 直接安装。根据 composer.json 中的声明它要求 PHP^7.3 || ^8.0并依赖phpdocumentor/reflection-common、phpstan/phpdoc-parser与doctrine/deprecations使用 PSR-4 规范将phpDocumentor\Reflection\命名空间映射到src目录。在项目根目录执行composer require phpdocumentor/type-resolver安装完成后通过 Composer 的自动加载即可直接使用无需额外配置require vendor/autoload.php;仓库的 examples 目录下所有示例脚本均采用require __DIR__ . /../vendor/autoload.php;的方式引入自动加载可直接运行参考。二、快速开始第一次解析安装之后创建一个\phpDocumentor\Reflection\TypeResolver实例调用其resolve()方法并传入要解析的类型字符串即可$typeResolver new \phpDocumentor\Reflection\TypeResolver(); $type $typeResolver-resolve(string); echo get_class($type); // phpDocumentor\Reflection\Types\String_resolve()的签名见 src/TypeResolver.php为resolve(string $type, ?Context $context null): Type返回值始终是实现phpDocumentor\Reflection\Type接口的值对象。例如string会被映射为\phpDocumentor\Reflection\Types\String_实例类名末尾的下划线用于避开 PHP 7 之前的保留字。TypeResolver 的真正威力在于它能把部分类名展开为完全限定类名但要做到这一点还需要一个额外的\phpDocumentor\Reflection\Types\Context类来告知解析器给定的类型表达式出现在哪个命名空间中以及该命名空间有哪些别名alias / import生效。Context 的详细用法见下文第四节。三、TypeResolver 能解析哪些类型从整体上看TypeResolver 可以解析以下几类表达式README 的 On Types and Element Names 一节有完整说明PHP 原生类型与伪类型如string、void对应 DocBlock 中的var string、return void复合类型composite如数组string[]对应var string[]联合类型compound如string|integer对应var string|integer数组表达式如(string|TypeResolver)[]对象或接口如TypeResolver或\phpDocumentor\Reflection\TypeResolver。注意如果想解析部分类名如上例中的TypeResolver需要额外提供 Context 信息详见第四节。3.1 关键字到值对象的映射TypeResolver内部维护了一张关键字 → 值对象类的映射表见 src/TypeResolver.php。下表是其中常用的一部分类型关键字解析出的值对象stringTypes\String_int/integerTypes\Integerbool/booleanTypes\Booleanfloat/real/doubleTypes\Float_arrayTypes\Array_objectTypes\Object_mixedTypes\Mixed_voidTypes\Void_nullTypes\Null_scalarTypes\Scalarcallable/callbackTypes\Callable_false/truePseudoTypes\False_/PseudoTypes\True_self/static/parentTypes\Self_/Types\Static_/Types\Parent_iterableTypes\Iterable_neverTypes\Never_list/non-empty-listPseudoTypes\List_/PseudoTypes\NonEmptyListclass-string/interface-stringTypes\ClassString/Types\InterfaceStringnumeric-string/literal-string/trait-stringPseudoTypes\NumericString/PseudoTypes\LiteralString/PseudoTypes\TraitStringpositive-int/negative-intPseudoTypes\PositiveInteger/PseudoTypes\NegativeIntegernon-empty-string/lowercase-string等PseudoTypes命名空间下的对应值对象关键字匹配是大小写不敏感的内部通过strtolower归一化后查表因此Integer与integer解析结果相同。3.2 解析联合类型resolve(string|integer)会返回一个\phpDocumentor\Reflection\Types\Compound值对象其中包含两个元素Types\String_和Types\Integer。Compound继承自AggregatedType以|作为类型分隔符见 src/Types/Compound.php表示该位置的值可能是其中任意一种类型。仓库的 examples/01-resolving-simple-types.php 演示了这一行为$typeResolver new TypeResolver(); // 得到一个 phpDocumentor\Reflection\Types\Compound 对象 var_export($typeResolver-resolve(string|integer)); // 输出字符串 string|int var_dump((string)$typeResolver-resolve(string|integer));需要注意的是值对象的__toString()输出可能与输入写法不完全一致如上例中integer会渲染为int这是由各值对象的字符串渲染规则决定的。3.3 解析可空类型PHP 7.1 引入了可空类型语法例如?string。TypeResolver 会先把?后面的原始类型按常规方式解析出来再用\phpDocumentor\Reflection\Types\Nullable将其包装对应源码中NullableTypeNode分支见 src/TypeResolver.php。Nullable对象提供了getActualType()方法用于取出被包装的真实类型见 src/Types/Nullable.php其__toString()则会渲染回?前缀形式$type $typeResolver-resolve(?string); // $type 是 Nullable 实例 echo get_class($type-getActualType()); // phpDocumentor\Reflection\Types\String_ echo (string)$type; // ?string这一行为在 tests/unit/TypeResolverTest.php 中也有对应测试解析?string得到Nullable实例其内部真实类型为String_字符串形式为?string。四、解析 FQSEN完全限定结构元素名FQSENFully Qualified Structural Element Name是 PSR-5 引入的另一种描述方式用于引用比类更深的代码元素——包括方法、属性、类常量以及函数和全局常量。使用\phpDocumentor\Reflection\FqsenResolver的resolve()方法即可解析$fqsenResolver new \phpDocumentor\Reflection\FqsenResolver(); $fqsen $fqsenResolver-resolve(\phpDocumentor\Reflection\FqsenResolver::resolve());这里传入的是一个完全限定的结构元素名包含完整命名空间、类名和元素名返回值为\phpDocumentor\Reflection\Fqsen值对象。与 TypeResolver 类似FqsenResolver的威力同样在于展开部分结构元素名——这同样需要Context提供命名空间与别名信息。FqsenResolver 支持解析以下形式的表达式见 README常量表达式see \MyNamespace\MY_CONSTANT函数表达式see \MyNamespace\myFunction()类表达式see \MyNamespace\MyClass接口表达式see \MyNamespace\MyInterfaceTrait 表达式see \MyNamespace\MyTrait类常量表达式see \MyNamespace\MyClass::MY_CONSTANT属性表达式see \MyNamespace\MyClass::$myProperty方法表达式see \MyNamespace\MyClass::myMethod()examples/03-resolving-all-elements.php 演示了方法与属性的 FQSEN 解析$context new Context(\phpDocumentor\Types); // 方法\phpDocumentor\Types\Types\Resolver::resolveFqsen() var_dump((string)$fqsenResolver-resolve(Types\Resolver::resolveFqsen(), $context)); // 属性\phpDocumentor\Types\Types\Resolver::$keyWords var_dump((string)$fqsenResolver-resolve(Types\Resolver::$keyWords, $context));五、解析部分类名Context 的角色假设你有这样一个文件仓库中的 examples/Classy.php 即为此示例带see标签的完整版见 READMEnamespace My\Example; use phpDocumentor\Reflection\Types; class Classy { /** * var Types\Context * see Classy::otherFunction() */ public function __construct($context) {} public function otherFunction(){} }现在你希望解析并展开var标签中的类型Types\Context和see标签中的元素名Classy::otherFunction()。要让解析器知道如何展开这些部分名称就必须为它们提供上下文实例化一个\phpDocumentor\Reflection\Types\Context把当前命名空间和生效的别名传进去。Context类本质上只保存两样东西见 src/Types/Context.php命名空间namespace当前 DocBlock 所处的命名空间命名空间别名表namespaceAliases别名 完全限定命名空间的映射对应源码中的use ... as ...导入。5.1 手动创建 Context$context new \phpDocumentor\Reflection\Types\Context( \My\Example, [ Types \phpDocumentor\Reflection\Types] );构造函数会对传入值做规范化见 src/Types/Context.php命名空间会去掉首尾的\global、default两个特殊值会被视为空命名空间别名表中的目标命名空间同样会去掉首尾的\统一为不带前导反斜杠的 QNN 格式。5.2 使用 ContextFactory 自动创建除了手动创建更常见的做法是使用\phpDocumentor\Reflection\Types\ContextFactory让它基于 Reflector 对象或源码内容自动分析命名空间与导入语句。基于反射对象创建——createFromReflector()支持ReflectionClass、ReflectionParameter、ReflectionMethod、ReflectionProperty与ReflectionClassConstant五类 Reflector见 src/Types/ContextFactory.php它们最终都会定位到声明类再读取类文件分析 import$contextFactory new \phpDocumentor\Reflection\Types\ContextFactory(); $context $contextFactory-createFromReflector( new ReflectionMethod(\My\Example\Classy, __construct) );基于文件内容创建——createForNamespace()接收目标命名空间和源码全文内部通过token_get_all()逐 token 扫描只收集目标命名空间作用域内的use语句并正确跳过类内部 trait 的use见 src/Types/ContextFactory.php$contextFactory new \phpDocumentor\Reflection\Types\ContextFactory(); $context $contextFactory-createForNamespace( \My\Example, file_get_contents(My/Example/Classy.php) );5.3 将 Context 传给解析器拿到 Context 后只需把它作为resolve()方法的第二个参数传入即可$typeResolver new \phpDocumentor\Reflection\TypeResolver(); $type $typeResolver-resolve(Types\Context, $context);此时你会得到一个\phpDocumentor\Reflection\Types\Object_对象。调用它的getFqsen()方法可以得到表示完整 FQSEN 的值对象即phpDocumentor\Reflection\Types\Context。为什么 FQSEN 要被Object_再包一层因为TypeResolver::resolve()的返回类型约定是Type接口而 FQSEN 本身并不代表一种类型。另外在某些场景下类型可能表示未指明具体元素的普通对象即object关键字这种情况下没有 FQSEN 可关联——Object_的包装正好同时覆盖这两种情况。再看see标签的例子解析方法的 FQSEN$fqsenResolver new \phpDocumentor\Reflection\FqsenResolver(); $fqsen $fqsenResolver-resolve(Classy::otherFunction(), $context);由于Classy是当前命名空间中的类其 FQSEN 会带上My\Example命名空间前缀最终得到一个指向\My\Example\Classy::otherFunction()的Fqsen对象。六、源码级原理解析流程与别名展开逻辑6.1 TypeResolver 的解析管线TypeResolver::resolve()的实际处理链路见 src/TypeResolver.php是对输入做trim()去空白空字符串会抛出InvalidArgumentException若未传入 Context则使用空命名空间的new Context()用phpstan/phpdoc-parser的Lexer对类型字符串做词法分析得到 token 流用TypeParser解析出 AST抽象语法树支持数组、数组形状、对象形状、可调用类型、泛型、联合、交集、可空、条件类型、偏移访问等节点createType()遍历 AST 节点逐个转换为对应的值对象。对于单个类型标识符IdentifierTypeNoderesolveSingleType()按优先级做三选一判断见 src/TypeResolver.php是关键字走关键字映射表实例化对应值对象是 FQSEN以\开头交给内部的FqsenResolver解析包装为Object_是部分结构元素名结合 Context 交给FqsenResolver展开包装为Object_。其中部分结构元素名的判定很直接不以\开头且不是已知关键字见 src/TypeResolver.php。6.2 FqsenResolver 的别名替换算法FqsenResolver::resolve()的逻辑见 src/FqsenResolver.php若输入以\开头直接作为 FQN 使用否则调用resolvePartialStructuralElementName()做部分名展开。展开算法的核心是见 src/FqsenResolver.php用\把输入拆成两段explode(\\, $type, 2)取第一段在第一段的别名表Context::getNamespaceAliases()中查找命中别名用别名对应的完全限定命名空间替换第一段拼接出 FQSEN未命中别名把当前命名空间Context::getNamespace()作为前缀拼接到最前面若命名空间为空全局空间则直接以\开头返回。以resolve(Types\Context, $context)为例别名表中有Types phpDocumentor\Reflection\Types第一段Types命中别名最终得到\phpDocumentor\Reflection\Types\Context。examples/02-resolving-classes.php 演示了同时命中别名与未命中别名的混合情况$context new Context(\phpDocumentor, [ m Mockery ]); // 展开为 \phpDocumentor\Types\Resolver|Mockery\MockInterface var_dump((string)$typeResolver-resolve(Types\Resolver|m\MockInterface, $context));其中Types\Resolver通过命名空间前缀展开为\phpDocumentor\Types\Resolverm\MockInterface通过别名展开为\Mockery\MockInterface。6.3 与反射/文件内容的配合实际工程中Context 几乎不需要手写——examples/04-discovering-the-context-using-class-reflection.php 展示了基于ReflectionClass自动发现上下文的完整流程require Classy.php; $contextFactory new ContextFactory(); $context $contextFactory-createFromReflector(new ReflectionClass(My\Example\Classy)); // 类\phpDocumentor\Reflection\Types\Resolver var_dump((string)$typeResolver-resolve(Types\Resolver, $context)); // 关键字 string 不受 Context 影响仍解析为 String_ var_dump((string)$typeResolver-resolve(string, $context)); // 属性\phpDocumentor\Reflection\Types\Resolver::$keyWords var_dump((string)$fqsenResolver-resolve(Types\Resolver::$keyWords, $context)); // 类\My\Example\string —— FqsenResolver 不认关键字一律按元素引用处理 var_dump((string)$fqsenResolver-resolve(string, $context));最后一个例子值得注意FqsenResolver与TypeResolver对string的处理截然不同。TypeResolver把string当作类型关键字解析为String_而FqsenResolver假定传入的一定是对另一个元素的引用因此会把string当作当前命名空间下的类名展开为\My\Example\string。这正体现了两个解析器的分工当关键字与类名存在冲突、且你确定目标是元素引用而非类型时用FqsenResolver可以强制按元素解析。ContextFactory的这两条路径基于反射、基于文件内容均有单元测试覆盖见 tests/unit/Types/ContextFactoryTest.php。七、下一步想快速跑通各类场景直接阅读并运行 examples 目录下的 6 个示例脚本覆盖简单类型、类解析、全部元素FQSEN、类反射/方法反射/文件内容发现 Context 等场景深入了解可解析的伪类型数组形状、整型范围、int-mask、key-of/value-of等可查看 src/PseudoTypes 目录及其在 tests/unit/PseudoTypes 中的测试完整的分辨能力清单与两大利器TypeResolver与FqsenResolver的对比可参阅 README.md本文对应的原始入门文档位于 docs/getting-started.rst。总结一下TypeResolver 的用法可以浓缩为三步——用 Composer 安装、实例化解析器调用resolve()、遇到部分类名时先通过ContextFactory或手动new Context()准备命名空间与别名并作为第二参数传入。掌握这三步你就能在自有工具链中可靠地把 DocBlock 类型与结构元素引用解析成程序可理解的值对象了。赞分享开发工具静态分析【免费下载链接】TypeResolverA PSR-5 based resolver of Class names, Types and Structural Element Names项目地址https://gitcode.com/gh_mirrors/ty/TypeResolver点击查看免费下载相关推荐phpDocumentor TypeResolver 完全指南用 PSR-5 语法解析 PHP 类型与结构元素名phpDocumentor TypeResolver 完全指南用 PSR 5 语法解析 PHP 类型与结构元素名 TypeResolver 是 phpDocu开发工具静态分析MindIE/stable_diffusion_v1.5终极指南AI绘图模型从入门到精通MindIE/stable_diffusion_v1.5终极指南AI绘图模型从入门到精通 MindIE/stable_diffusion_v1.5是一款强大的Label Studio OCR 发票预标注完整实战从零构建 BIO 格式 NER 训练语料Label Studio OCR 发票预标注完整实战从零构建 BIO 格式 NER 训练语料 这个模板用 Label Studio 完成 OCR 发票预标注开发工具静态分析上一篇5分钟上手Hourglass这款Windows倒计时工具如何让你每天多出2小时下一篇生物医学领域的图可解释性应用Awesome Explainable Graph Reasoning 专业指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
