BetterReflection 不能靠 autoload 加载类来反射,因为它必须在不触发自动加载、不实例化、不执行任何代码的前提下解析 PHP 源码;否则会破坏静态性,引发依赖问题或副作用。

为什么 BetterReflection 不能靠 autoload 加载类来反射?
因为 BetterReflection 的核心能力是「在不触发自动加载、不实例化、不执行任何代码的前提下解析 PHP 源码」。一旦你用 new ClassReflector(...) 或调用某个类,Composer 的 autoloader 就可能把目标类提前加载进来——这会破坏反射的“静态性”,甚至引发依赖未就绪、循环 require、或副作用(比如 __construct() 被执行)。所以必须绕过 Composer 的类自动加载机制,只让它负责提供 BetterReflection 自身的代码。
安装时禁用 BetterReflection 的 autoload 规则
默认 composer require roave/better-reflection 会把它的 PSR-4 映射注册进 Composer autoloader,导致后续任何 class_exists('SomeClass') 或 new 操作都可能触发加载。要阻止这点:
- 运行
composer require roave/better-reflection --no-autoloader(注意--no-autoloader) - 手动在
composer.json中删掉或注释掉roave/better-reflection下的autoload和autoload-dev字段(它通常带"psr-4": {"Roave\BetterReflection\": "src/"}) - 执行
composer dump-autoload确保变更生效
这样 Composer 就只加载 BetterReflection 自己的类(靠 vendor/autoload.php 里已有的路径),而不会把你的目标文件当作可自动加载的类去处理。
用 SourceLocator 指向原始文件,而非类名
BetterReflection 反射的关键不是传类名,而是传源码路径。常见错误是写 $reflector->reflectClass('MyAppFoo') —— 这会触发 autoloader;正确做法是:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
$sourceLocator = new SingleFileSourceLocator('/path/to/Foo.php');
// 或目录级
$sourceLocator = new DirectoriesSourceLocator(['/path/to/src']);
$reflector = new ClassReflector($sourceLocator);
$reflection = $reflector->reflect('Foo'); // 注意:这里是类名字符串,但 source locator 已锁定文件位置,不走 autoload
要点:
-
SingleFileSourceLocator和DirectoriesSourceLocator都基于文件系统路径,不依赖class_exists或require_once -
reflect('Foo')中的'Foo'是类名标识符,BetterReflection会从 locator 提供的源码中提取 AST 解析,不是去加载它 - 确保路径真实存在且可读;如果路径含命名空间别名(如
MyAppFoo),需确认源码文件里namespace MyApp;正确声明
避免 eval、include 和 class_alias 干扰
有些反射逻辑会偷偷引入运行时加载,比如:
- 用
eval()执行生成的类定义(BetterReflection默认不用,但自定义SourceLocator实现可能误用) - 在反射前
include了目标文件(这会让 PHP 解析并注册类,破坏“纯静态”前提) - 用了
class_alias()或function_exists()等触发 autoload 的函数
安全做法是:所有目标 PHP 文件保持“未被 include/require/eval”,只交给 BetterReflection 的 SourceLocator 去读取内容并解析 AST。它内部用 file_get_contents() + nikic/php-parser,完全隔离于 PHP 的运行时类表。
真正难把控的是路径拼接和命名空间映射——比如 DirectoriesSourceLocator 扫描时,Foo 和 MyAppFoo 对应哪个文件,取决于你是否设置了 PhpParserVisitor 或自定义 SourceLocator 的命名空间解析规则。这里没报错,但反射结果为空,往往就是路径或命名空间对不上。

















