跳转到主要内容

什么是 PHP 属性

PHP 属性(PHP Attributes)是 PHP 8.0 引入的原生元数据语法。可以对类、方法、属性、函数等以 #[AttributeName] 形式附加元信息。 Laravel 在框架内部积极采用 PHP 属性,可以以声明式的方式配置 Job 与 Eloquent Model。Laravel 13(v13.2.0)中队列属性还开始接受 enum。相较于以往通过类属性或方法覆写来设置,使用属性可以写出更易读、更简洁的代码。
// 传统写法
class ProcessOrder implements ShouldQueue
{
    public string $queue = 'orders';
    public string $connection = 'redis';
    public int $tries = 3;
    public array $backoff = [30, 60, 120];
}

// 使用属性的写法
#[Queue('orders')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
}
属性在 PHP 8.0 及以上可用。Laravel 13 要求 PHP 8.3 及以上,因此在所有环境中都能使用属性。

队列相关属性

队列 Job 相关的属性都位于 Illuminate\Queue\Attributes 命名空间。

#[Queue] — 指定队列名

指定 Job 派发的默认队列名。
use Illuminate\Queue\Attributes\Queue;

#[Queue('emails')]
class SendWelcomeEmail implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
从 v13.2.0 起,除了字符串也可以传入 enum。
enum QueueName: string
{
    case Emails = 'emails';
    case Orders = 'orders';
    case Notifications = 'notifications';
}

#[Queue(QueueName::Emails)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
#[Queue] 属性的 target 被设置为 Attribute::TARGET_CLASS,因此只能用在类上。

#[Connection] — 指定连接

指定 Job 使用的默认队列连接。
use Illuminate\Queue\Attributes\Connection;

#[Connection('sqs')]
class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function handle(): void
    {
        // ...
    }
}
同样也支持 enum。
enum QueueConnection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
}

#[Connection(QueueConnection::Sqs)]
class ProcessPayment implements ShouldQueue
{
    // ...
}

#[Backoff] — 指定重试的退避时间

指定 Job 失败后重试前的等待时间(秒)。可传多个值为不同次重试指定不同等待时间(支持可变参数)。
use Illuminate\Queue\Attributes\Backoff;

// 固定等待时间(所有重试共 60 秒)
#[Backoff(60)]
class SendEmail implements ShouldQueue
{
    // ...
}

// 每次重试不同的等待时间(指数退避)
#[Backoff(30, 60, 120)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
Backoff 类的实现,它接收可变参数。
// Illuminate\Queue\Attributes\Backoff 的实现
#[Attribute(Attribute::TARGET_CLASS)]
class Backoff
{
    public array|int $backoff;

    public function __construct(array|int ...$backoff)
    {
        $this->backoff = count($backoff) === 1 ? $backoff[0] : $backoff;
    }
}
单值时保存为 int,多值时保存为 array

#[Tries] — 指定重试次数

指定 Job 失败时的最大重试次数。
use Illuminate\Queue\Attributes\Tries;

#[Tries(5)]
class ProcessPayment implements ShouldQueue
{
    // ...
}

#[Timeout] — 指定超时时间

指定 Job 的最大执行时间(秒),超过则会被强制终止。
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(120)]
class GenerateReport implements ShouldQueue
{
    // ...
}

#[MaxExceptions] — 指定允许的异常次数

发生指定次数以上的异常时,将 Job 视为失败。通常与 #[Tries] 搭配使用。
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Tries;

#[Tries(10)]
#[MaxExceptions(3)]
class ProcessWebhook implements ShouldQueue
{
    // ...
}

#[UniqueFor] — 指定唯一周期

指定阻止 Job 重复执行的锁定周期(秒)。与 ShouldBeUnique 一起使用。
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Queue\Attributes\UniqueFor;

#[UniqueFor(3600)]
class SyncUserData implements ShouldQueue, ShouldBeUnique
{
    // ...
}

#[DeleteWhenMissingModels] — 模型不存在时删除

当 Job 依赖的 Eloquent 模型不存在时,将该 Job 视为删除(跳过)而不是失败。
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;

#[DeleteWhenMissingModels]
class SendOrderConfirmation implements ShouldQueue
{
    public function __construct(
        protected Order $order,
    ) {}

    public function handle(): void
    {
        // 当 $order 不存在时,该 Job 会被删除
    }
}

#[WithoutRelations] — 排除关联

在 Job 序列化时不包含模型的关联,可以让入队数据更小。
use Illuminate\Queue\Attributes\WithoutRelations;

#[WithoutRelations]
class ExportUser implements ShouldQueue
{
    public function __construct(
        protected User $user,
    ) {}
}

#[FailOnTimeout] — 超时视为失败

发生超时时把 Job 记录为失败(默认情况下超时不会记录为失败)。
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\Timeout;

#[Timeout(30)]
#[FailOnTimeout]
class ProcessLongTask implements ShouldQueue
{
    // ...
}

组合多个队列属性

将这些属性组合可以以声明方式配置 Job 的行为。
use Illuminate\Queue\Attributes\Backoff;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;
use Illuminate\Queue\Attributes\FailOnTimeout;
use Illuminate\Queue\Attributes\MaxExceptions;
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Timeout;
use Illuminate\Queue\Attributes\Tries;

#[Queue('payments')]
#[Connection('redis')]
#[Tries(3)]
#[Backoff(30, 60, 120)]
#[Timeout(60)]
#[MaxExceptions(2)]
#[DeleteWhenMissingModels]
class ProcessPayment implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        protected Order $order,
    ) {}

    public function handle(PaymentService $payment): void
    {
        $payment->charge($this->order);
    }
}

Eloquent 相关属性

Eloquent 模型属性位于 Illuminate\Database\Eloquent\Attributes 命名空间。Laravel 13 中新增了大量属性。

#[ScopedBy] — 指定全局作用域

以属性形式指定要自动应用到模型上的全局作用域类。支持继承,通过 IS_REPEATABLE 标志可以指定多个作用域。
use Illuminate\Database\Eloquent\Attributes\ScopedBy;

#[ScopedBy(ActiveScope::class)]
class User extends Model
{
    // 无需在 booted() 中注册作用域
}
要绑定多个作用域时,可重复使用属性或以数组形式传入。
// 重复指定(IS_REPEATABLE)
#[ScopedBy(ActiveScope::class)]
#[ScopedBy(VerifiedScope::class)]
class User extends Model
{
}

// 用数组一次指定
#[ScopedBy([ActiveScope::class, VerifiedScope::class])]
class User extends Model
{
}
与传统的 booted() 方法对比。
// 传统写法
class User extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveScope);
        static::addGlobalScope(new VerifiedScope);
    }
}

#[ObservedBy] — 指定 Observer

以属性形式指定模型关联的 Observer 类。与 ScopedBy 一样是 IS_REPEATABLE
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy(UserObserver::class)]
class User extends Model
{
}
也可以指定多个 Observer。
#[ObservedBy(UserObserver::class)]
#[ObservedBy(AuditObserver::class)]
class User extends Model
{
}
无需再在 AppServiceProvider 中注册。
// 传统写法(AppServiceProvider)
public function boot(): void
{
    User::observe(UserObserver::class);
}

#[UseEloquentBuilder] — 指定自定义 Query Builder

以属性形式指定模型所使用的自定义 Eloquent Builder。
use Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder;

#[UseEloquentBuilder(UserQueryBuilder::class)]
class User extends Model
{
}
namespace App\Builders;

use Illuminate\Database\Eloquent\Builder;

class UserQueryBuilder extends Builder
{
    public function active(): static
    {
        return $this->where('active', true);
    }

    public function verified(): static
    {
        return $this->whereNotNull('email_verified_at');
    }
}
// 使用示例(可以类型安全地调用自定义方法)
$users = User::query()->active()->verified()->get();

#[CollectedBy] — 指定自定义 Collection

以属性形式指定模型作为集合时使用的自定义 Collection 类。
use Illuminate\Database\Eloquent\Attributes\CollectedBy;

#[CollectedBy(UserCollection::class)]
class User extends Model
{
}
namespace App\Collections;

use Illuminate\Database\Eloquent\Collection;

class UserCollection extends Collection
{
    public function admins(): static
    {
        return $this->filter(fn (User $user) => $user->is_admin);
    }

    public function active(): static
    {
        return $this->filter(fn (User $user) => $user->active);
    }
}

#[Table] — 一次指定表相关配置

在一个属性中指定表名、主键、时间戳等多个表相关配置。
use Illuminate\Database\Eloquent\Attributes\Table;

#[Table(name: 'system_users', key: 'user_id', timestamps: false)]
class SystemUser extends Model
{
}
Table 属性可配置的选项如下。
参数对应属性说明
name$table表名
key$primaryKey主键列名
keyType$keyType主键类型('int''string' 等)
incrementing$incrementing主键是否自增
timestamps$timestamps是否启用时间戳
dateFormat$dateFormat日期格式

#[Scope] — 将方法定义为本地作用域

可以把没有 scope 前缀的方法定义为 Eloquent 本地作用域。
use Illuminate\Database\Eloquent\Attributes\Scope;

class User extends Model
{
    #[Scope]
    public function active(Builder $query): void
    {
        $query->where('active', true);
    }

    #[Scope]
    public function verified(Builder $query): void
    {
        $query->whereNotNull('email_verified_at');
    }
}
// 传统写法方法名需带「scope」前缀
// public function scopeActive(Builder $query): void

// 使用属性后方法名直接作为作用域名
User::query()->active()->verified()->get();

#[UseFactory] — 指定 Factory 类

以属性形式指定模型使用的自定义 Factory 类。
use Illuminate\Database\Eloquent\Attributes\UseFactory;

#[UseFactory(UserFactory::class)]
class User extends Model
{
}

其他 Eloquent 属性

属性说明
#[Fillable(...$attributes)]指定批量赋值允许的列
#[Guarded(...$attributes)]指定批量赋值保护的列
#[Unguarded]禁用批量赋值保护
#[Hidden(...$attributes)]指定序列化时排除的列
#[Visible(...$attributes)]指定序列化时包含的列
#[Appends(...$attributes)]指定序列化时追加的 accessor
#[Touches(...$relations)]指定更新时同步更新 updated_at 的关联
#[WithoutTimestamps]禁用时间戳
#[WithoutIncrementing]禁用主键自增
#[DateFormat(format: '...')]指定日期格式
#[UsePolicy(policyClass: '...')]指定关联的 Policy 类
#[UseResource(resourceClass: '...')]指定关联的 API Resource 类
#[UseResourceCollection(resourceCollectionClass: '...')]指定关联的 Resource Collection 类

Enum 支持(v13.2.0 新增)

在 v13.2.0 中,#[Queue]#[Connection] 开始接受 enum。你可以用 PHP enum 而非字符串字面量来类型安全地指定队列与连接。
// 队列名的 enum 定义
enum Queue: string
{
    case Default = 'default';
    case High = 'high';
    case Low = 'low';
    case Emails = 'emails';
    case Orders = 'orders';
}

// 连接的 enum 定义
enum Connection: string
{
    case Redis = 'redis';
    case Sqs = 'sqs';
    case Database = 'database';
    case Sync = 'sync';
}
use Illuminate\Queue\Attributes\Queue;
use Illuminate\Queue\Attributes\Connection;

// 使用 enum 的类型安全指定
#[Queue(Queue::Orders)]
#[Connection(Connection::Redis)]
class ProcessOrder implements ShouldQueue
{
    // ...
}
使用 enum 可以防止队列名或连接名的拼写错误,也能获得 IDE 补全。适合在应用中统一管理队列与连接名。

与传统类属性的对比

属性的优点

  • 声明式 — 一眼就能看出 Job 的行为
  • 类型安全 — 使用 enum 能获得 IDE 补全与类型检查
  • 与继承兼容 — 子类可覆盖父类的属性
  • 代码更精简 — 无需属性声明或方法覆写

属性的缺点

  • 不能设置动态值 — 属性参数只能是编译期常量,无法使用变量或配置文件的值
  • 需要熟悉 — 团队可能需要熟悉 PHP 8 的属性语法

需要动态值时

想在运行时决定值时,仍使用传统的方法覆写。
class ProcessOrder implements ShouldQueue
{
    // 动态 backoff 用方法定义
    public function backoff(): array
    {
        return [
            config('queue.backoff.first'),
            config('queue.backoff.second'),
        ];
    }
}
属性会在 PHP 编译阶段解析,无法使用 config()env() 等运行时值。需要动态配置时请继续使用类属性或方法。

实现机制

Laravel 内部使用 Reflection API 读取属性。当 Queue Worker 派发 Job 时,ReadsQueueAttributes trait(包含在 InteractsWithQueue 中)会通过反射发现属性并把值写入相应属性。
// 内部读取示意(简化)
$reflection = new ReflectionClass($job);
$attributes = $reflection->getAttributes(Queue::class);

foreach ($attributes as $attribute) {
    $instance = $attribute->newInstance();
    $job->queue = $instance->queue instanceof UnitEnum
        ? $instance->queue->value
        : $instance->queue;
}
Eloquent 模型的属性同样通过反射在 Model::booted() 对应时机被读取。

下一步

中级:队列与 Job

学习 Laravel 队列系统的基本用法。

PHP Reflection API

详解 Laravel 用于读取属性的 Reflection API 的机制。
最后修改于 2026年7月13日