推荐 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,用于清理各代码库中积累属性读取相关的样板代码。

相关推荐
白菜欣3 小时前
Linux — 进程控制
android·linux·运维
踩着两条虫3 小时前
「AI + 低代码」的可视化设计器
开发语言·前端·低代码·设计模式·架构
JoneBB3 小时前
ABAP Webservice连接
运维·开发语言·数据库·学习
即使再小的船也能远航4 小时前
【Python】安装
开发语言·python
Irissgwe4 小时前
类与对象(三)
开发语言·c++·类和对象·友元
雪度娃娃5 小时前
转向现代C++——优先选用nullptr而不是0和NULL
开发语言·c++
weixin199701080165 小时前
【保姆级教程】淘宝/天猫商品详情 API(item_get)接入指南:Python/Java/PHP 调用示例与 JSON 返回值解析
java·python·php
萌新小码农‍5 小时前
python装饰器
开发语言·前端·python
KK溜了溜了5 小时前
Python从入门到精通
服务器·开发语言·python
故事和你916 小时前
洛谷-【图论2-1】树5
开发语言·数据结构·c++·算法·动态规划·图论