The Features Nobody Tweets About
Enhanced Developer Experience
What Are PHP Attributes, Really?
If you’re newer to PHP, attributes (introduced in PHP 8.0, documented at php.net/manual ) are a structured metadata syntax that lets you attach declarative information directly to classes, methods, functions, and properties.
Think of them as “annotations that PHP actually knows about.”
Before PHP 8, developers used docblock comments like `@param` or `@route` — and tools parsed those comments at runtime (slow, fragile, no type checking). Native attributes are first-class language constructs. They’re fast, typed, and IDE-friendly.
// Old way: docblock comment (parsed at runtime, no type safety)
/**
* @deprecated
* @param string $name
*/
public function oldMethod(string $name) {}
// New way: native PHP attribute (parsed by PHP itself, type-safe)
#[Deprecated]
public function newMethod(string $name): void {}
Moving From Class Properties to Attributes
In older Laravel versions, configuring a Model, Job, or Command required a specific set of public properties defined in the class body. You had to know which property controlled which behavior — and the documentation was your only guide.
Here’s a typical Laravel 11/12 Job:
// Old approach — Laravel 11/12 style
class ProcessPodcast implements ShouldQueue
{
public int $tries = 3;
public int $timeout = 120;
public string $queue = 'high-priority';
public bool $deleteWhenMissingModels = true;
public function handle(): void
{
// job logic
}
}
// New approach — Laravel 13 with PHP Attributes
use Illuminate\Queue\Attributes\DeleteWhenMissingModels;
#[Tries(3)]
#[Timeout(120)]
#[OnQueue('high-priority')]
#[DeleteWhenMissingModels]
class ProcessPodcast implements ShouldQueue
{
public function handle(): void
{
// job logic — clean, no noise
}
}
The job body is now only business logic. The configuration is declared right at the class signature, where it belongs. This is not cosmetic — it’s a meaningful shift in how code communicates intent.
Note
Attributes in Models, Jobs, and Commands
Models
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
use App\Observers\UserObserver;
use App\Scopes\ActiveScope;
#[ObservedBy(UserObserver::class)]
#[ScopedBy(ActiveScope::class)]
class User extends Model
{
// No need for boot() method to register observers and scopes
// The attribute declares it right at the class level
}
// Laravel 11 — boot() method required
protected static function boot(): void
{
parent::boot();
static::observe(UserObserver::class);
static::addGlobalScope(new ActiveScope);
}
That boot method is gone. The intent is now at the top of the class where you’d naturally look first.
Artisan Commands
use Illuminate\Console\Attributes\AsCommand;
#[AsCommand(name: 'app:send-digest', description: 'Send weekly email digest')]
class SendWeeklyDigest extends Command
{
public function handle(): void
{
// command logic
}
}
Scheduled Tasks
use Illuminate\Console\Attributes\Schedule;
#[Schedule('0 8 * * 1')] // Every Monday at 8am
class SendWeeklyDigest extends Command
{
public function handle(): void {}
}
The "Clean Code" Impact
Robert C. Martin (Uncle Bob) in *Clean Code* (2008, Prentice Hall) argues that good code should read like well-written prose. Configuration tucked inside `boot()` methods, service providers, or scattered across separate files violates what he calls the proximity principle — related things should live close to each other.
PHP Attributes enforce proximity by design.
Here’s what I’ve seen in real codebases:
| Problem (Before Attributes) | Solution (With Attributes) |
|---|---|
Observer registration buried in AppServiceProvider |
#[ObservedBy(...)] on the Model |
| Queue config in 3 separate places | #[OnQueue(...)] on the Job class |
Command scheduling hidden in Kernel.php |
#[Schedule(...)] on the Command |
Global scopes applied in boot() |
#[ScopedBy(...)] on the Model |
For business leaders
Are Attributes Always Better?
Use attributes when
- The configuration is tightly coupled to a single class
- You want the class to be self-documenting
- You're working on a new project or a fresh module
Stick with properties when
- You have dynamic configuration (computed at runtime)
- You're in a shared legacy codebase where consistency matters more than modernity
- Your team is not yet comfortable with attribute syntax
Explore project snapshots or discuss custom web solutions.
Programs must be written for people to read, and only incidentally for machines to execute.
Thank You for Spending Your Valuable Time
I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
Frequently Asked Questions
No. Laravel 13 is fully backward compatible. The attribute syntax is optional. You can migrate incrementally — new classes use attributes, old classes keep their properties.
Similar concept, different implementation. PHP Attributes are native language constructs (not parsed from comments) and are inspected via PHP's `ReflectionAPI`. They're faster and more reliable than legacy docblock annotations.
No. PHPStorm and VS Code (with PHP Intelephense or Laravel Idea) have supported PHP 8.0 attributes since 2021. Laravel 13 attributes are fully supported.
The official Laravel 13 documentation at laravel.com/docs/13.x covers all attribute-based APIs. Laravel News published a detailed breakdown at laravel-news.com/laravel-13-released
Absolutely. Attributes are actually easier to learn than boot() method patterns. If you understand the concept of "metadata attached to a class," you're already halfway there. php.net/manual/en/language.attributes.overview.php has beginner-friendly documentation.
Comments are closed