Signatur
Beschreibung
Seit PHP 8.0 können Entwickler mit der #[Attribute]-Syntax strukturierte, maschinenlesbare Metadaten direkt in den Quellcode einbetten. Um eine eigene Klasse als Attribut nutzbar zu machen, muss sie selbst mit #[Attribute] ausgezeichnet werden. Die eingebaute Klasse Attribute ist dabei der Schlüssel: Sie validiert, an welchen Deklarations-Typen ein Attribut erlaubt ist.
Über den optionalen Flags-Parameter des Attribute-Konstruktors kann präzise gesteuert werden, ob ein eigenes Attribut z. B. nur für Klassen (Attribute::TARGET_CLASS), nur für Methoden (Attribute::TARGET_METHOD) oder für mehrere Ziele gleichzeitig erlaubt ist. Die Flags werden bitweise kombiniert. Ohne Angabe gilt Attribute::TARGET_ALL.
Zum Auslesen von Attributen zur Laufzeit wird die Reflection-API verwendet: ReflectionClass::getAttributes(), ReflectionMethod::getAttributes() usw. liefern ReflectionAttribute-Objekte zurück, über die per newInstance() die Attribut-Klasse instanziiert werden kann.
Attribute eignen sich hervorragend für Framework-Funktionalität wie Routing, Dependency-Injection, Validierungs-Regeln oder ORM-Mapping — überall dort, wo bisher Docblock-Annotations oder separate Konfigurationsdateien verwendet wurden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $flags | int | Attribute::TARGET_ALL | Bitmaske aus Attribute::TARGET_*-Konstanten, die festlegt, an welchen Deklarations-Typen das Attribut erlaubt ist. Mehrere Flags werden mit | kombiniert. |
Rückgabewert
Beispiele
Eigenes Attribut definieren und auslesen
<?php
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)]
class Route
{
public function __construct(
public readonly string $path,
public readonly string $method = 'GET'
) {}
}
#[Route('/api/users')]
class UserController
{
#[Route('/api/users/{id}', method: 'POST')]
public function update(int $id): void {}
}
// Attribute per Reflection auslesen
$ref = new ReflectionClass(UserController::class);
$attrs = $ref->getAttributes(Route::class);
foreach ($attrs as $attr) {
$route = $attr->newInstance();
echo $route->path . ' [' . $route->method . ']' . PHP_EOL;
}
$methodRef = new ReflectionMethod(UserController::class, 'update');
foreach ($methodRef->getAttributes(Route::class) as $attr) {
$route = $attr->newInstance();
echo $route->path . ' [' . $route->method . ']' . PHP_EOL;
}
Validierungs-Attribut für Eigenschaften
<?php
#[Attribute(Attribute::TARGET_PROPERTY)]
class NotBlank
{
public function __construct(
public readonly string $message = 'Darf nicht leer sein.'
) {}
}
class UserDto
{
#[NotBlank]
public string $name = '';
#[NotBlank(message: 'E-Mail ist ein Pflichtfeld.')]
public string $email = '';
}
// Einfacher Validator
function validate(object $dto): array
{
$errors = [];
$ref = new ReflectionClass($dto);
foreach ($ref->getProperties() as $prop) {
foreach ($prop->getAttributes(NotBlank::class) as $attr) {
$constraint = $attr->newInstance();
if (trim((string)$prop->getValue($dto)) === '') {
$errors[$prop->getName()] = $constraint->message;
}
}
}
return $errors;
}
$dto = new UserDto();
print_r(validate($dto));
// Wichtig · Fallstricke
Verfügbare TARGET-Konstanten:
Attribute::TARGET_CLASSAttribute::TARGET_FUNCTIONAttribute::TARGET_METHODAttribute::TARGET_PROPERTYAttribute::TARGET_CLASS_CONSTANTAttribute::TARGET_PARAMETERAttribute::TARGET_ALL(Standard, alle obigen kombiniert)
Standardmäßig darf dasselbe Attribut nur einmal pro Deklaration verwendet werden. Mit Attribute::IS_REPEATABLE (per Bitmaske hinzufügen) ist Mehrfachverwendung erlaubt.
Das Auslesen von Attributen über die Reflection-API hat Laufzeit-Overhead. Für performance-kritische Szenarien sollte das Ergebnis gecacht werden, z. B. per APCu oder einem dedizierten Metadaten-Cache.
Attribut-Klassen werden beim Einsatz von newInstance() tatsächlich instanziiert — Fehler im Konstruktor (z. B. Typfehler) führen daher zu Ausnahmen zur Laufzeit.