惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News | PayPal Newsroom
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
SecWiki News
SecWiki News
Know Your Adversary
Know Your Adversary
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
NISL@THU
NISL@THU
WordPress大学
WordPress大学
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Threat Research - Cisco Blogs
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
Security Archives - TechRepublic
Security Archives - TechRepublic
有赞技术团队
有赞技术团队
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
V
V2EX
G
GRAHAM CLULEY
TaoSecurity Blog
TaoSecurity Blog
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Latest news
Latest news
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
T
Troy Hunt's Blog
S
Schneier on Security
I
Intezer
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
爱范儿
爱范儿
The Register - Security
The Register - Security
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
美团技术团队
B
Blog RSS Feed

博客园 - 与f

微软的人机验证 docker运行hermes agent python使用docx库对在word中的表格合并操作 Python 项目创建+依赖管理+版本控制 百度飞桨PaddleOCR-VL识别发票图片输出json格式 大模型微调后导出,部署服务然后后端调用 openclaw的一些聊天配置 docker 部署 openclaw llama.cpp编译 微信小程序显示ai返回的markdown或html C#多端桌面程序(可跨平台) 主流 MySQL 热备份方案 基于SRS(Simple Realtime Server)+ CDN边缘节点加速3个nginx做文件分发1个nginx做负载均衡 一份基于SRS(Simple Realtime Server)+ CDN边缘节点srs edge加速的万人级直播部署清单 HLS(HTTP Live Streaming)标准的视频及加密和解密播放 HLS(HTTP Live Streaming)标准的视频及加密 使用Dockerfile创建一个hyperf容器做为开发环境 docker的hyperf框架docker-compose一键安装 通过vm虚拟中的docker 环境做开发 ubuntu 安装后ssh 连接到服务器 lvm的概念和操作(linux) centos 服务器查找一些字符串 Centos8网络配置小工具 Keepalive实现一个高可用负载均衡场景 Keepalived详解:原理、编译安装与高可用集群配置
PHP Attributes 注解
与f · 2025-12-25 · via 博客园 - 与f

PHP Attributes ,php8.0之后的特性。

PHP Attributes(注解)是 PHP 8.0 引入的一项重要特性,用于在代码中以结构化、类型安全的方式为类、方法、属性、函数、参数等添加元数据(metadata)。它取代了过去通过注释(如 DocBlock)来传递元数据的“约定式”做法,使元数据成为语言的一等公民。

一、什么是 PHP Attributes?

Attributes(注解) 是一种原生语法,允许开发者将附加信息(元数据)直接附加到程序元素上,例如:

  • 类(class)
  • 方法(method)
  • 属性(property)
  • 函数(function)
  • 参数(parameter)
  • 常量(constant)

这些元数据可以在运行时通过反射(Reflection)读取和使用。

✅ 关键点:Attributes 不是注释,而是可被解析和使用的实际代码结构。

二、为什么引入 Attributes?

在 PHP 8 之前,框架(如 Doctrine、Symfony)广泛使用 DocBlock 注解(Annotations) 来实现 ORM 映射、路由定义、验证规则等功能。例如:

/**
 * @Entity
 * @Table(name="users")
 */
class User {}

但这类注解存在以下问题:

  • 非标准语法(依赖第三方库解析)
  • 无类型检查
  • IDE 支持有限
  • 容易拼写错误且难以调试

PHP Attributes 提供了原生、类型安全、IDE 友好的替代方案。

三、Attribute 的基本语法

1. 定义一个 Attribute

使用 #[Attribute] 标记一个类为属性类:

<?php
use Attribute;

#[Attribute]
class MyAttribute
{
    public function __construct(public string $value = '') {}
}

注意:必须导入 Attribute 类(use Attribute;),它是 PHP 内置类。

2. 使用 Attribute

#[MyAttribute('Hello')]
class MyClass
{
    #[MyAttribute('World')]
    public string $name = '';
}

3. 多个 Attribute

可以堆叠多个:

#[MyAttribute('A')]
#[MyAttribute('B')]
class Test {}

或使用数组形式(PHP 8.0+):

#[MyAttribute('A'), MyAttribute('B')]
class Test {}

四、Attribute 的目标(Targets)

你可以限制 Attribute 只能用在特定位置,通过 flags 参数:

#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Route
{
    public function __construct(public string $path) {}
}

常用目标常量:

TARGET_CLASSTARGET_FUNCTION 函数 TARGET_METHOD 方法 TARGET_PROPERTY 属性 TARGET_PARAMETER 参数 TARGET_CLASS_CONSTANT 类常量 IS_REPEATABLE 允许重复使用
常量说明

示例:只允许用于方法,并可重复:

五、运行时读取 Attributes(反射)

使用 PHP 的反射 API 获取 Attributes:

$reflection = new ReflectionClass(MyClass::class);
$attributes = $reflection->getAttributes();

foreach ($attributes as $attribute) {
    // 获取 Attribute 实例
    $instance = $attribute->newInstance();
    echo $instance->value; // 输出 'Hello'
}

⚠️ 注意:newInstance() 会实例化 Attribute 类,因此构造函数参数必须匹配。

也可以获取原始参数而不实例化:

$attr = $reflection->getAttributes(MyAttribute::class)[0];
$args = $attr->getArguments(); // ['Hello']

六、实际应用示例

1. 方法参数上写注解,进行参数校验

#[Attribute(Attribute::TARGET_PARAMETER)]
class MaxLength {
    public function __construct(public int $length) {}
}

function greet(#[MaxLength(10)] string $name) {
    // 验证逻辑可通过反射在调用前执行,如果匹配失败抛出异常,正常方法中的逻辑先不执行
}

模仿框架中反射处理注解,(直接实例化调用greet方法,注解是完全失效的)

<?php

#[Attribute(Attribute::TARGET_PARAMETER)]
class MaxLength {
    public function __construct(public int $length) {}
}

function greet(#[MaxLength(10)] string $name, string $message) {
    echo "Hello, {$name}! Message: {$message}\n";
}

/**
 * 通用的函数参数注解校验函数(优化版:显式判断注解是否存在)
 * @param string $funcName 要调用的函数名
 * @param array $args 函数入参数组(按参数顺序传递)
 * @throws InvalidArgumentException 校验失败时抛出异常
 */
function validateFunctionArgs(string $funcName, array $args): void {
    $reflectionFunc = new ReflectionFunction($funcName);
    $parameters = $reflectionFunc->getParameters();

    foreach ($parameters as $paramIndex => $param) {
        // 1. 跳过无传入值的参数
        if (!isset($args[$paramIndex])) {
            continue;
        }

        // 2. 显式获取 MaxLength 注解,判断是否存在
        $maxLengthAttributes = $param->getAttributes(MaxLength::class);
        // 核心优化:显式判断注解是否存在,不存在则直接跳过当前参数
        if (empty($maxLengthAttributes)) {
            continue;
        }

        // 3. 注解存在时,才执行校验逻辑
        /** @var MaxLength $maxLengthAttr */
        $maxLengthAttr = $maxLengthAttributes[0]->newInstance();
        $maxLength = $maxLengthAttr->length;
        $paramValue = $args[$paramIndex];

        // 4. 仅对字符串类型参数做长度校验(避免非字符串参数报错)
        if (is_string($paramValue) && mb_strlen($paramValue) > $maxLength) {
            throw new InvalidArgumentException(
                "参数「{$param->getName()}」长度不能超过 {$maxLength}(当前长度:" . mb_strlen($paramValue) . ")"
            );
        }
    }
}

function callFunctionWithValidation(string $funcName, array $args): void {
    validateFunctionArgs($funcName, $args);
    call_user_func_array($funcName, $args);
}

// -------------------------- 测试示例(验证无注解参数跳过) --------------------------
try {
    // 测试:name加了注解(长度限制10),message无注解(长度超10也不校验)
    callFunctionWithValidation('greet', ['Alice', '这是一段超过10个字符的消息内容']);
    
    // 测试:name长度超10,触发校验异常
    callFunctionWithValidation('greet', ['ThisIsALongName123', '正常消息']);
} catch (InvalidArgumentException $e) {
    echo "校验失败:{$e->getMessage()}\n";
}

2. 路由定义(类似 Symfony)

#[Attribute(Attribute::TARGET_METHOD)]
class Route {
    public function __construct(
        public string $path,
        public string $method = 'GET'
    ) {}
}

class UserController
{
    #[Route('/user', 'GET')]
    public function list() {}
}

路由解析器可通过反射读取 Route 属性注册路由。(具体可以看看下面引用的文章)

3. ORM 映射(类似 Doctrine)

#[Attribute(Attribute::TARGET_PROPERTY)]
class Column {
    public function __construct(
        public string $type,
        public bool $nullable = false
    ) {}
}

class User
{
    #[Column('string', nullable: false)]
    public string $email;
}

ORM 框架可在启动时扫描这些属性生成数据库映射。

七、与 DocBlock 注解对比

语法 注释(字符串) 原生代码 类型安全 ❌ ✅ IDE 支持 依赖插件 原生支持(自动补全、跳转) 性能 需解析字符串 直接编译为 AST 节点 可重复 通常支持 需显式声明 IS_REPEATABLE 标准化 无标准 PHP 官方标准(RFC)
特性DocBlock 注解PHP Attributes

八、兼容性与版本要求

  • 最低版本:PHP 8.0+
  • 不兼容 PHP 7.x(无法降级使用)

九、最佳实践

  1. 命名清晰:Attribute 类名应表达其用途(如 RouteCacheableValidateEmail)。
  2. 限制目标:始终使用 TARGET_* 限制使用位置,避免误用。
  3. 避免复杂逻辑:Attribute 类应保持简单,仅存储数据。
  4. 缓存反射结果:反射较慢,生产环境应缓存解析结果。

十、总结

PHP Attributes 是现代 PHP 开发的重要工具,它:

  • 提供了标准化的元数据机制
  • 提升了代码可读性与安全性
  • 为框架开发带来更强大的能力
  • 推动 PHP 向更现代化的语言演进

如果你正在使用 PHP 8+,强烈建议在新项目中优先使用 Attributes 替代传统注解。

📚 官方文档参考:

文档有千问生成

引用 :https://www.cnblogs.com/catchadmin/p/19395584