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

使用 Attribute Reader

假设有一个携带 Route 属性的控制器,目标是获取该属性的实例。使用原生 PHP 反射的写法如下:

复制代码
$reflection = new ReflectionClass(MyController::class);
$attributes = $reflection->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF);

$route = null;
if (count($attributes) > 0) {
    $route = $attributes[0]->newInstance();
}

这段代码长达五行,且仍需处理属性不存在的情况。使用 php-attribute-reader 后简化为:

复制代码
use Spatie\Attributes\Attributes;

$route = Attributes::get(MyController::class, Route::class);

单行完成。属性不存在时返回 null,无需额外的异常处理。

读取方法属性

从方法读取属性时,原生反射的繁琐程度进一步加剧。以下示例试图获取控制器 index 方法的 Route 属性:

复制代码
$reflection = new ReflectionMethod(MyController::class, 'index');
$attributes = $reflection->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF);

$route = null;
if (count($attributes) > 0) {
    $route = $attributes[0]->newInstance();
}

样板代码重复出现,仅反射类有所不同。该包通过专用方法统一处理各类目标:

复制代码
Attributes::onMethod(MyController::class, 'index', Route::class);
Attributes::onProperty(User::class, 'email', Column::class);
Attributes::onConstant(Status::class, 'ACTIVE', Label::class);
Attributes::onParameter(MyController::class, 'show', 'id', FromRoute::class);

全类扫描

原生反射在整类范围内查找属性时最为繁琐。假设某表单类的多个属性均携带 Validate 属性,原生 PHP 的实现大致如下:

复制代码
$results = [];
$class = new ReflectionClass(MyForm::class);

foreach ($class->getProperties() as $property) {
    foreach ($property->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
        $results[] = ['attribute' => $attr->newInstance(), 'target' => $property];
    }
}

foreach ($class->getMethods() as $method) {
    foreach ($method->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
        $results[] = ['attribute' => $attr->newInstance(), 'target' => $method];
    }
    foreach ($method->getParameters() as $parameter) {
        foreach ($parameter->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
            $results[] = ['attribute' => $attr->newInstance(), 'target' => $parameter];
        }
    }
}

foreach ($class->getReflectionConstants() as $constant) {
    foreach ($constant->getAttributes(Validate::class, ReflectionAttribute::IS_INSTANCEOF) as $attr) {
        $results[] = ['attribute' => $attr->newInstance(), 'target' => $constant];
    }
}

对于常见的属性扫描需求,上述代码量显得过于庞大。使用该包后简化为:

复制代码
$results = Attributes::find(MyForm::class, Validate::class);

foreach ($results as $result) {
    $result->attribute; // 已实例化的属性对象
    $result->target;    // 反射对象
    $result->name;      // 例如 'email', 'handle.request'
}

所有返回的属性均为实例化对象,子类通过 IS_INSTANCEOF 自动匹配,目标不存在时返回 null 而非抛出异常。

实际应用

该包已被 Spatie 旗下的多个开源项目采用,包括 laravel-responsecachelaravel-event-sourcinglaravel-markdown,用于清理各代码库中积累属性读取相关的样板代码。

相关推荐
指尖的爷8 小时前
C++头文件的作用
开发语言·c++
keykey6.8 小时前
反向传播与梯度下降:神经网络如何学习
开发语言·人工智能·深度学习·机器学习
阿洛学长8 小时前
最新PHPStudy安装教程(小皮V8.1)
php
Jun6268 小时前
QT(5)-第三方日志系统
开发语言·数据库·qt
冰暮流星8 小时前
javascript建立对象之构造函数
开发语言·javascript·ecmascript
keykey6.8 小时前
PyTorch 入门实战:从张量到训练循环
开发语言·人工智能·深度学习·机器学习
消失的旧时光-19438 小时前
Kotlin 协程设计思想(七):为什么 Kotlin 要设计 SupervisorJob 和 supervisorScope?
android·开发语言·kotlin
Full Stack Developme8 小时前
SpringMVC multipart 文件上传
java·开发语言
得一录9 小时前
ModuleNotFoundError: No module named ‘llama_index.llms
开发语言·人工智能
j7~9 小时前
【C++】类和对象(下)--详解之再探构造函数,友元,static成员,类型转换等
开发语言·c++·类型转换·友元·匿名对象·内部类·编译器优化