技术 / 可调用反射
一个简化任何可调用或构造函数反射的便捷库。PHP8就绪!
Requires
- php: ^7.1|^8.0
Requires (Dev)
- peridot-php/peridot: ^1.19
README
技术可调用反射
Technically\CallableReflection
是一个简化任何 callable
反射的便捷库。它提供了一个统一的接口,无缝支持 PHP8 联合类型,用于读取可调用参数。它还允许您通过 call()
和 apply()
简单地调用可调用,支持命名参数。
特性
- 统一且简化的反射可调用参数接口
- 无依赖项
- PHP 8.0 就绪(支持联合类型提示;请参阅以下示例)
- PHP 7.1+ 兼容
- 语义化版本控制
- 测试
安装
使用 Composer 包管理器将 Technically\CallableReflection 添加到您的项目中
composer require technically/callable-reflection
示例
反射可调用属性
<?php $function = function (string $abstract, Closure|string|null $concrete): mixed { // function body }; $reflection = CallableReflection::fromCallable($function); var_dump($reflection->isFunction()); // false var_dump($reflection->isMethod()); // false var_dump($reflection->isClosure()); // true [$p1, $p2] = $reflection->getParameters(); var_dump($p2->getName()); // "concrete" var_dump($p2->isNullable()); // true var_dump($p2->isOptional()); // false var_dump($p2->hasTypes()); // true [$t1, $t2, $t3] = $p2->getTypes(); var_dump($t1->isScalar()); // false var_dump($t1->isClassName()); // true var_dump($t1->getType()); // "Closure" var_dump($t2->isScalar()); // true var_dump($t2->isClassName()); // false var_dump($t2->getType()); // "string" var_dump($t3->isNull()); // true var_dump($t3->isScalar()); // false var_dump($t3->isClassName()); // false var_dump($t3->getType()); // "null"
反射任意类构造函数
<?php final class MyService { public function __construct(LoggerInterface $logger, bool $debug = false) { // ... } } $reflection = CallableReflection::fromConstructor(MyService::class); var_dump($reflection->isConstructor()); // true var_dump($reflection->isFunction()); // false var_dump($reflection->isMethod()); // false var_dump($reflection->isClosure()); // false [$p1, $p2] = $reflection->getParameters(); var_dump($p1->getName()); // "logger" var_dump($p1->isNullable()); // false var_dump($p1->isOptional()); // false var_dump($p1->hasTypes()); // true var_dump($p2->getName()); // "debug" var_dump($p2->isNullable()); // false var_dump($p2->isOptional()); // true var_dump($p2->hasTypes()); // true
检查值是否满足参数类型声明
$function = function (int|string $value = null): mixed { // function body }; $reflection = CallableReflection::fromCallable($function); [$param] = $reflection->getParameters(); var_dump($param->satisfies(null)); // true var_dump($param->satisfies(1)); // true var_dump($param->satisfies('Hello')); // true var_dump($param->satisfies(2.5)); // false var_dump($param->satisfies([])); // false var_dump($param->satisfies(true)); // false
通过反射调用可调用
$function = function (string $abstract, Closure|string|null $concrete): mixed { // function body }; $reflection = CallableReflection::fromCallable($function); // 1) call with positional parameters $result = $reflection->call(LoggerInterface::class, MyLogger::class); // 1) call with named parameters $result = $reflection->call(concrete: MyLogger::class, abstract: LoggerInterface::class); // 2) call with positional parameters array $result = $reflection->apply([LoggerInterface::class, MyLogger::class]); // 3) call with named parameters array $result = $reflection->apply(['concrete' => MyLogger::class, 'abstract' => LoggerInterface::class]); // 4) call with mixed named and positional parameters array $result = $reflection->apply([LoggerInterface::class, 'concrete' => MyLogger::class]); // 5) CallableReflection is a callable by itself $result = $reflection(LoggerInterface::class, MyLogger::class);
通过反射调用构造函数
final class MyService { public function __construct(LoggerInterface $logger, bool $debug = false) { // ... } } $reflection = CallableReflection::fromConstructor(MyService::class); $service = $reflection->call(new NullLogger()); // or alternatively: // $service = $reflection->apply(['logger' => new NullLogger()]); assert($service instanceof MyService);
这比反射 Closure::fromCallable()
好在哪里
这个库的功能与标准 Closure::fromCallable()
的反射功能有些相似。如果这对您的用例足够,我建议使用标准代码。
然而,这个库提供了一些额外的价值
-
它统一了与所有类型的可调用的交互,包括类构造函数。这实际上是我考虑的主要用例——构建一个依赖注入服务容器。
例如,您不能使用 Closure::fromCallable() 创建一个新实例
$reflection = new ReflectionFunction(Closure::fromCallable([MyRemoteService::class, '__construct'])); $service = $reflection->call($token); // (!) doesn't work
但您可以使用这个库调用构造函数
$reflection = CallableReflection::fromConstructor(MyRemoteService::class); $service = $reflection->call($token);
-
它可以知道并告知反射了哪种类型可调用,而 Closure::fromCallable() 则丢失了这些信息。这可能对某些用例很重要,或者不一定。取决于场景。
-
它还有一些方便的额外获取器和检查器。例如
satisfies()
、isOptional()
、isNull()
、isScalar()
等。 -
我还发现这个 API 比较直观和方便(当然,这是主观的,可以讨论),因为原生反射 API 由于向后兼容性添加而略受污染。
-
在性能方面,我相信这个实现肯定比使用原生代码要慢。尽管我没有测试以提供确切的数字。它基本上在做几乎相同的事情,但额外添加了一些代码——只是在反射 API 之上操作。
变更日志
此项目的所有重要更改都将记录在 CHANGELOG 文件中。
鸣谢
由 👾 Ivan Voskoboinyk 实现。