v13.39.0 und implementiert ein eigenes Attribut, das die Konfiguration courier liest. Wenn das eingebaute #[Config('courier.timeout')] bereits ausreicht, müssen Sie keine eigenen Attribute hinzufügen. Ziehen Sie sie in Betracht, wenn Ihr Paket eigene Schlüsselkonventionen oder Auflösungsregeln als Teil seiner API anbieten soll.
Die Aufgabe eines Attributs ist es, den injizierten Wert festzulegen
Attribute, dieIlluminate\Contracts\Container\ContextualAttribute implementieren, werden vom Laravel-Container beim Auflösen von Parametern gelesen. An die nutzende Klasse wird nicht das Attributobjekt selbst übergeben, sondern der Wert, den der Resolver zurückgibt.
Normale Bindings und die eingebauten Attribute finden Sie unter Service-Container. Kontextuelle Attribute sind ein anderer Mechanismus als Attribute wie
Bind, Singleton und Scoped, die Bindings für Klassen oder Interfaces deklarieren.
Ein Attribut zum Lesen der Paketkonfiguration erstellen
Standardkonfiguration registrieren
Legen Sie zunächst inconfig/courier.php des Pakets die Standardwerte an.
register() von src/CourierServiceProvider.php zusammen. Die automatische Registrierung des Providers ist unter Interne Struktur der automatischen Paket-Erkennung beschrieben, das Veröffentlichen und den Umgang mit dem Konfigurations-Cache finden Sie unter Paketkonfiguration zusammenführen und cachen.
ContextualAttribute implementieren
Definieren Sie in src/Attributes/CourierConfig.php ein Attribut, das dem Konfigurationsschlüssel courier. voranstellt.
- Die Instanz des Attributs, das am Parameter angebracht ist.
- Den Container, der die Abhängigkeit auflöst.
- Ein
ReflectionParameter, der das Auflösungsziel darstellt.
#[CourierConfig(default: 30)] int $timeout verweist auf courier.timeout. Wenn dieselbe Konfiguration auch nach einer Umbenennung des Parameters auf Nutzerseite gelesen werden soll, geben Sie den Schlüssel explizit an, etwa #[CourierConfig('timeout', default: 30)].
ContextualAttribute ist zum Zeitpunkt der Prüfung ein Marker-Interface ohne deklarierte Methoden. Daher garantiert implements allein weder die Existenz noch die Signatur eines Resolvers. Wenn Sie wie oben resolve() bereitstellen, müssen Sie für dieses Attribut kein normales bind() registrieren.
Die nutzende Klasse über den Container erzeugen
Versehen Siesrc/CourierOptions.php mit den Attributen.
$options->timeout gleich 45 und $options->retries gleich 3. Das Attribut liest nur die Konfiguration und führt keine fachliche Validierung durch, etwa ob es sich um eine positive Ganzzahl handelt oder ob ein zulässiger Höchstwert eingehalten wird. Auch Typdeklarationen ersetzen keine Validierung von Konfigurationswerten.
Die Auflösungsreihenfolge im Konstruktor nachvollziehen
Container::resolveDependencies() bestimmt den Wert jedes Parameters in der folgenden Reihenfolge.
- Einen Überschreibungswert verwenden, der z. B. über
makeWith()übergeben wurde und zum Parameternamen passt. - Mit
Util::getContextualAttributeFromDependency()nach einem kontextuellen Attribut suchen und es auflösen. - Wenn das Ergebnis des Attributs
nullist, zur normalen Auflösung von primitiven Werten oder Klassen übergehen. - Nach der Nachbearbeitung des Attributs den ermittelten Wert an den Konstruktor übergeben.
makeWith() sind keine Konfigurationsschlüssel, sondern Parameternamen der nutzenden Klasse.
timeout unabhängig vom Konfigurationswert 5. Gibt das Attribut dagegen einen Wert ungleich null zurück, hat das Ergebnis des Attributs Vorrang vor einem normalen when()->needs('$timeout')->give(...). Testen Sie daher auch, ob das Hinzufügen eines Attributs im Paket bestehende kontextuelle Bindings der Nutzer verändern kann.
Util ermittelt per Reflection die Attribute, die ContextualAttribute implementieren, und wählt das erste davon für die Auflösung aus. Wenn Sie mehrere verschiedene kontextuelle Attribute nebeneinander angeben, werden die Resolver nicht nacheinander kombiniert.
Registrierte Handler haben Vorrang vor resolve()
Container::resolveFromAttribute() sucht zuerst mit dem Klassennamen des Attributs als Schlüssel nach einem Handler, der mit whenHasAttribute() registriert wurde. Gibt es keinen, wird resolve() des Attributs verwendet. Fehlt beides, wird eine BindingResolutionException geworfen.
whenHasAttribute() ist keine Einstellung pro nutzender Klasse, sondern pro Attributtyp. Eine erneute Registrierung für dieselbe Attributklasse überschreibt den vorherigen Handler. Wenn ein Paket die Handler anderer Pakete oder der eingebauten Laravel-Attribute bedingungslos ändert, wirkt sich das auf die gesamte nutzende Anwendung aus. Legen Sie das Standardverhalten eigener Attribute in resolve() ab und behandeln Sie Änderungen über Handler als expliziten Erweiterungspunkt.
make(), call() und direkte Aufrufe verhalten sich nicht gleich
Auch app()->call() löst Attribute auf, aber der normale BoundMethod-Pfad fügt das Ergebnis eines gefundenen Attributs unverändert den Argumenten hinzu. Anders als bei der Konstruktorauflösung wird bei einem Ergebnis von null nicht auf die Auflösung des Standardwerts zurückgegriffen.
Ein Beispiel, das ohne weitere kontextuelle Bindings den nicht existierenden Schlüssel courier.missing liest:
Auch
new CourierOptions(timeout: 5, retries: 1) ist eine normale Instanziierung durch PHP. Das bloße Anbringen eines Attributs fügt direkten new-Aufrufen oder Methodenaufrufen keine Dependency Injection hinzu.
Wenn Konstruktoren und Callables dasselbe Standardverhalten benötigen, verlassen Sie sich nicht nur auf den Standardwert des Parameters, sondern definieren Sie einen Standardwert im Attribut, etwa
#[CourierConfig('timeout', default: 30)]. Dieses default greift, wenn der Konfigurationsschlüssel nicht existiert. Es ersetzt keinen vorhandenen Schlüssel, dessen Wert null ist. Bei APIs, in denen null selbst ein bedeutungsvoller Wert ist, prüfen Sie beide Auflösungswege getrennt.Erneute Injektion von Werten und Lebensdauer von Services trennen
Das Attribut liest die Konfiguration zum Zeitpunkt der Parameterauflösung. Es ist keine Funktion, die Eigenschaften eines bereits erzeugtenCourierOptions überwacht oder aktualisiert.
Wenn Sie CourierOptions beispielsweise als Singleton registrieren, behält diese Instanz die zuerst injizierten Werte. Auch wenn Sie danach die Konfiguration ändern und ein normales make() aufrufen, ändern sich die Werte derselben Instanz nicht.
Auch bei Attributen, die anfragebezogene Informationen lesen, bestimmt das Attribut selbst nicht die Lebensdauer als Singleton oder Scoped. Wählen Sie die Lebensdauer der Services, die injizierte Werte oder Objekte halten, gemäß Lebenszyklus-Design für Paket-Services. Betrachten Sie das Neuerzeugen des Konfigurations-Caches getrennt von der Aktualisierung langlebiger Prozesse und bereits erzeugter Services.
Auflösungswege testen
Tests für das Attribut allein lassen sich wie in den offiziellen Tests mit einem neuenContainer und Illuminate\Config\Repository schreiben. Das folgende PHPUnit-Beispiel setzt eine Testumgebung voraus, in der die oben definierten Klassen CourierConfig, CourierOptions und NullableTimeoutOptions geladen sind.
7 zurückgibt, dient nur zum Testen der Priorität. Innerhalb dieses Containers wirkt es sich auf jede Auflösung desselben Attributs aus, einschließlich retries.
Dieser Unit-Test allein deckt die Integration mit der Provider-Registrierung des Pakets, mergeConfigFrom() und dem Konfigurations-Cache nicht ab. Prüfen Sie zusätzlich in der Anwendungsumgebung aus Laravel-Pakete mit Orchestra Testbench testen die Zustände vor der Veröffentlichung, nach der Veröffentlichung und mit Konfigurations-Cache.
Prüfpunkte vor dem Release
- Behandeln Sie den Klassennamen des Attributs, die Namen der Konstruktorparameter, die Konfigurationsschlüssel und den Rückgabetyp als öffentliche API. Achten Sie auch auf Änderungen an
keyunddefault, die als benannte Argumente verwendet werden. - Wenn bei fehlendem Schlüssel der Parametername verwendet wird, prüfen Sie, ob eine Umbenennung des Parameters in der nutzenden Klasse die referenzierte Konfiguration verändert.
- Testen Sie bestehende Bindings ohne Attribut, die Auflösung mit Attribut und das Überschreiben per Parametername jeweils separat.
- Testen Sie
null, fehlende Konfiguration und ungeeignete Typen sowohl mitmake()als auch mitcall(). - Stellen Sie sicher, dass Singletons und langlebige Worker keine veralteten Werte halten.
- Prüfen Sie bei einer Aktualisierung der unterstützten Laravel-Version die Argumente des Resolvers, die Priorität und die Nachbearbeitung der Attribute erneut anhand des offiziellen Quellcodes und der Tests.
Verwandte Seiten
Paketkonfiguration zusammenführen und cachen
Prüfen Sie die Grenzen zwischen Standardkonfiguration, veröffentlichter Konfiguration und Cache-Aktualisierung.
Lebenszyklus-Design für Paket-Services
Wählen Sie die Lebensdauer von Services, die injizierte Werte halten.
Container-Tags und Plugin-Aggregation in Paketen
Lernen Sie das eingebaute Tag-Attribut und die Aggregation registrierter Implementierungen kennen.
PHP Reflection API
Verstehen Sie, wie Metadaten von Attributen und Parametern gelesen werden.
Herangezogene Primärquellen
- Offizielle Laravel-Dokumentation: Defining custom attributes
- Offizielle Laravel-Dokumentation: Paketkonfiguration
- Container: resolveDependencies(), resolveFromAttribute(), whenHasAttribute() und Nachbearbeitung von Attributen
- BoundMethod: Parameterauflösung für Callables
- Util: Erkennung von ContextualAttribute per Reflection
- ContextualAttribute: Marker-Interface
- Config: Eingebautes Konfigurationsattribut
- Config Repository: get() und das Abrufen von Konfigurationswerten
- Arr: Standardwert von get() und Umgang mit vorhandenem null
- Offizielle Tests: Resolver von Attributen, registrierte Handler, ReflectionParameter und Auflösung mit call