注解语法
概述
PHP 注解(Attributes)使用 #[...] 语法声明,支持命名空间、参数传递、重复注解和目标限制。掌握注解的完整语法是正确声明和使用注解的基础。本章将系统讲解注解的各种语法形式和声明规则。
基本语法
注解使用 #[ClassName(params)] 格式声明,放置在目标声明之前的行。
基础概念
声明位置
注解必须放在目标声明之前,可以独占一行,也可以在同一行。注解可以使用多个 #[...] 块,也可以用逗号分隔写在同一个块中。
命名空间
注解类可以(且应该)使用命名空间。如果注解类在当前命名空间中,可以省略命名空间前缀。
参数
注解的参数通过注解类的构造函数传递,支持命名参数和位置参数。
语法与代码
基本声明
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute]
class MyAnnotation
{
public function __construct(public readonly string $value = '') {}
}
// 独占一行的注解
#[MyAnnotation]
function myFunction(): void {}
// 带参数的注解
#[MyAnnotation(value: 'hello')]
function anotherFunction(): void {}多个注解的声明方式
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute]
class Route { public function __construct(public string $path = '') {} }
#[Attribute]
class Middleware { public function __construct(public string $name = '') {} }
// 方式一:多个 #[...] 块
#[Route(path: '/api/users')]
#[Middleware(name: 'auth')]
function getUsers(): array { return []; }
// 方式二:单个块中逗号分隔
#[Route(path: '/api/posts'), Middleware(name: 'cache')]
function getPosts(): array { return []; }
// 方式三:混合(PHP 8.0+ 支持嵌套)
#[Route(path: '/api'), Middleware(name: 'rate-limit')]
function apiEndpoint(): void {}命名空间与 use 导入
php
<?php
declare(strict_types=1);
namespace App\Http\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class Get {}
#[Attribute(Attribute::TARGET_METHOD)]
class Post {}
// 使用时
namespace App\Http\Controllers;
use App\Http\Attributes\Get;
use App\Http\Attributes\Post;
class UserController
{
#[Get]
public function index(): array { return []; }
#[Post]
public function store(): array { return []; }
}注解参数语法
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute(Attribute::TARGET_CLASS)]
class Entity
{
public function __construct(
public readonly string $table,
public readonly string $repository = '',
public readonly bool $readonly = false
) {}
}
// 位置参数
#[Entity('users')]
class User {}
// 命名参数(PHP 8.0+)
#[Entity(table: 'products', readonly: true)]
class Product {}
// 混合参数
#[Entity('orders', repository: 'OrderRepository')]
class Order {}注解中不使用命名参数的写法
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute]
class Config
{
public function __construct(
public readonly string $key,
public readonly mixed $default = null
) {}
}
// 仅传递第一个参数
#[Config('app.name')]
class App {}
// 传递所有参数
#[Config('app.debug', false)]
class DebugConfig {}重复注解
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_METHOD)]
class Role
{
public function __construct(public readonly string $name) {}
}
// 同一方法上使用多次相同注解
class AdminController
{
#[Role(name: 'admin')]
#[Role(name: 'super-admin')]
public function dashboard(): string
{
return 'Admin Dashboard';
}
}IS_REPEATABLE
如果不声明 Attribute::IS_REPEATABLE,同一目标上使用多次相同注解会引发错误。
目标限制
php
<?php
declare(strict_types=1);
use Attribute;
// 仅可用于类
#[Attribute(Attribute::TARGET_CLASS)]
class Entity { public function __construct(public string $table) {} }
// 仅可用于方法
#[Attribute(Attribute::TARGET_METHOD)]
class Route { public function __construct(public string $path) {} }
// 仅可用于属性
#[Attribute(Attribute::TARGET_PROPERTY)]
class Column { public function __construct(public string $type) {} }
// 仅可用于函数参数
#[Attribute(Attribute::TARGET_PARAMETER)]
class Validate { public function __construct(public string $rule) {} }
// 多个目标(位或组合)
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Cacheable {}枚举 case 上的注解(PHP 8.1+)
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute(Attribute::TARGET_ENUM_CASE)]
class Description
{
public function __construct(public readonly string $text) {}
}
enum Status: int
{
#[Description(text: '活跃状态')]
case Active = 1;
#[Description(text: '禁用状态')]
case Inactive = 2;
#[Description(text: '待审核状态')]
case Pending = 3;
}函数参数上的注解
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute(Attribute::TARGET_PARAMETER)]
class MaxLength
{
public function __construct(public readonly int $length) {}
}
#[Attribute(Attribute::TARGET_PARAMETER)]
class NotBlank {}
function validateInput(
#[NotBlank]
#[MaxLength(length: 255)]
string $username,
#[NotBlank]
#[MaxLength(length: 100)]
string $email
): void {
// 验证逻辑...
}详细说明
注解目标常量
| 常量 | 值 | 说明 |
|---|---|---|
Attribute::TARGET_CLASS | 1 | 类、接口、trait、枚举 |
Attribute::TARGET_FUNCTION | 2 | 函数 |
Attribute::TARGET_METHOD | 4 | 方法 |
Attribute::TARGET_PROPERTY | 8 | 属性 |
Attribute::TARGET_CLASS_CONSTANT | 16 | 类常量 |
Attribute::TARGET_PARAMETER | 32 | 函数/方法参数 |
Attribute::TARGET_ALL | 63 | 所有目标 |
Attribute::IS_REPEATABLE | 64 | 允许在同一目标上重复使用 |
注解的嵌套
php
<?php
declare(strict_types=1);
use Attribute;
#[Attribute]
class Group
{
public function __construct(public readonly string $name) {}
}
#[Attribute]
class Veto
{
public function __construct(public readonly string $reason) {}
}
#[Group(name: 'admin'), Veto(reason: 'deprecated')]
class OldAdminController {}实战示例
实战:构建完整的 API 注解系统
php
<?php
declare(strict_types=1);
namespace App\Annotations;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
class HttpGet
{
public function __construct(public readonly string $path = '/') {}
}
#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
class Middleware
{
public function __construct(public readonly string $name) {}
}
#[Attribute(Attribute::TARGET_METHOD)]
class RequestBody
{
public function __construct(public readonly string $class) {}
}
// 使用
namespace App\Controllers;
use App\Annotations\HttpGet;
use App\Annotations\Middleware;
use App\Annotations\RequestBody;
class ApiUserController
{
#[HttpGet(path: '/api/v1/users')]
#[Middleware(name: 'auth')]
#[Middleware(name: 'throttle')]
public function list(): array
{
return [];
}
#[HttpGet(path: '/api/v1/users/{id}')]
#[Middleware(name: 'auth')]
public function show(int $id): array
{
return ['id' => $id];
}
}注意事项
注解类必须是有效的 PHP 类
注解类必须是可以实例化的。抽象类、接口、trait 不能直接用作注解。
注解参数必须是常量表达式
注解的参数必须是 PHP 中有效的常量表达式(字面量、常量、枚举值等),不能是变量或函数调用。
php
<?php
declare(strict_types=1);
const CACHE_TTL = 3600;
#[Entity(table: 'users')] // 正确:字面量
#[Cache(prefix: 'user_', ttl: CACHE_TTL)] // 正确:常量
// #[Cache(prefix: strtolower('X'))] // 错误:不能使用函数调用最佳实践
- 使用
readonly类:注解类应该是readonly的(PHP 8.2+),确保不可变性。 - 指定目标:始终在
#[Attribute]中明确指定目标限制。 - 使用构造函数验证:在构造函数中验证参数的合法性。
- 统一放在独立命名空间:如
App\Attributes。 - 命名清晰:注解类名应清晰表达其用途。
php
<?php
declare(strict_types=1);
namespace App\Attributes;
use Attribute;
#[Attribute(Attribute::TARGET_METHOD)]
readonly class HttpPost
{
public function __construct(public readonly string $path = '/') {}
}