Start · Sprachen · PHP · Referenz · Attribute

Attribute

Klasse

Die <code>Attribute</code>-Klasse markiert eine eigene Klasse als PHP-Attribut und ermöglicht so das Hinzufügen strukturierter Metadaten zu Klassen, Methoden, Funktionen, Parametern, Eigenschaften und Konstanten.

seit PHP 8.0.0 Kategorie: misc

Signatur

class Attribute

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

Typ

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;
}
/api/users [GET] /api/users/{id} [POST]

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));
Array ( [name] => Darf nicht leer sein. [email] => E-Mail ist ein Pflichtfeld. )

// Wichtig · Fallstricke

Verfügbare TARGET-Konstanten:

  • Attribute::TARGET_CLASS
  • Attribute::TARGET_FUNCTION
  • Attribute::TARGET_METHOD
  • Attribute::TARGET_PROPERTY
  • Attribute::TARGET_CLASS_CONSTANT
  • Attribute::TARGET_PARAMETER
  • Attribute::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.