找回密码
 立即注册
首页 业界区 业界 推荐 PHP 属性(Attributes) 简洁读取 API 扩展包 ...

推荐 PHP 属性(Attributes) 简洁读取 API 扩展包

剽达崖 6 小时前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包

PHP 8.0 引入的 Attributes(属性)为类、方法、属性、常量和参数添加结构化元数据提供了便利方式。尽管概念设计合理,但读取这些属性所需的反射 API 却显得过于冗长。原本简单的一行操作,往往要写成多行样板代码。若需在某个类中查找某属性的全部使用位置,还得编写层层嵌套的循环。
Spatie 近期发布的 php-attribute-reader 包提供了一套干净的静态 API,专门解决上述问题。
使用 Attribute Reader

假设有一个携带 Route 属性的控制器,目标是获取该属性的实例。使用原生 PHP 反射的写法如下:
  1. $reflection = new ReflectionClass(MyController::class);
  2. $attributes = $reflection->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF);
  3. $route = null;
  4. if (count($attributes) > 0) {
  5.     $route = $attributes[0]->newInstance();
  6. }
复制代码
这段代码长达五行,且仍需处理属性不存在的情况。使用 php-attribute-reader 后简化为:
  1. use Spatie\Attributes\Attributes;
  2. $route = Attributes::get(MyController::class, Route::class);
复制代码
单行完成。属性不存在时返回 null,无需额外的异常处理。
读取方法属性

从方法读取属性时,原生反射的繁琐程度进一步加剧。以下示例试图获取控制器 index 方法的 Route 属性:
  1. $reflection = new ReflectionMethod(MyController::class, 'index');
  2. $attributes = $reflection->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF);
  3. $route = null;
  4. if (count($attributes) > 0) {
  5.     $route = $attributes[0]->newInstance();
  6. }
复制代码
样板代码重复出现,仅反射类有所不同。该包通过专用方法统一处理各类目标:
  1. Attributes::onMethod(MyController::class, 'index', Route::class);
  2. Attributes::onProperty(User::class, 'email', Column::class);
  3. Attributes::onConstant(Status::class, 'ACTIVE', Label::class);
  4. Attributes::onParameter(MyController::class, 'show', 'id', FromRoute::class);
复制代码
全类扫描

原生反射在整类范围内查找属性时最为繁琐。假设某表单类的多个属性均携带 Validate 属性,原生 PHP 的实现大致如下:
  1. $results = [];
  2. $class = new ReflectionClass(MyForm::class);
  3. foreach ($class->getProperties() as $property) {
  4.     foreach ($property->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
  5.         $results[] = ['attribute' => $attr->newInstance(), 'target' => $property];
  6.     }
  7. }
  8. foreach ($class->getMethods() as $method) {
  9.     foreach ($method->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
  10.         $results[] = ['attribute' => $attr->newInstance(), 'target' => $method];
  11.     }
  12.     foreach ($method->getParameters() as $parameter) {
  13.         foreach ($parameter->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
  14.             $results[] = ['attribute' => $attr->newInstance(), 'target' => $parameter];
  15.         }
  16.     }
  17. }
  18. foreach ($class->getReflectionConstants() as $constant) {
  19.     foreach ($constant->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
  20.         $results[] = ['attribute' => $attr->newInstance(), 'target' => $constant];
  21.     }
  22. }
复制代码
对于常见的属性扫描需求,上述代码量显得过于庞大。使用该包后简化为:
  1. $results = Attributes::find(MyForm::class, Validate::class);
  2. foreach ($results as $result) {
  3.     $result->attribute; // 已实例化的属性对象
  4.     $result->target;    // 反射对象
  5.     $result->name;      // 例如 'email', 'handle.request'
  6. }
复制代码
所有返回的属性均为实例化对象,子类通过 IS_INSTANCEOF 自动匹配,目标不存在时返回 null 而非抛出异常。
实际应用

该包已被 Spatie 旗下的多个开源项目采用,包括 laravel-responsecache、laravel-event-sourcing 和 laravel-markdown,用于清理各代码库中积累属性读取相关的样板代码。
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册