PHP-Instanz von Traits

Lesezeit: 5 Minuten

Benutzer-Avatar
xpedobearx

Was ist der richtige Weg, um zu überprüfen, ob eine Klasse ein bestimmtes Merkmal verwendet?

  • Macht es? Meines Wissens nach nicht ($x instanceof SomeTrait wird immer falsch sein).

    – Kuba

    12. Oktober 2016 um 12:13 Uhr

  • Mögliches Duplikat von How to get used traits in php-class?

    – ar34z

    3. November 2016 um 8:19 Uhr

  • instanceof ist bei einem Merkmal immer falsch. Merkmale sind keine Instanz von etwas.

    – Raffaello

    12. Dezember 2016 um 10:31 Uhr

Benutzer-Avatar
Kuba Birecki

Obwohl Sie nichts davon abhält, Methoden zu verwenden, um festzustellen, ob eine Klasse ein Merkmal verwendet, besteht der empfohlene Ansatz darin, Merkmale mit Schnittstellen zu paaren. Sie hätten also:

class Foo implements MyInterface
{
    use MyTrait;
}

Wo MyTrait ist eine Implementierung von MyInterface.

Dann suchen Sie nach der Schnittstelle anstelle von Merkmalen wie folgt:

if ($foo instanceof MyInterface) {
    ...
}

Und Sie können auch einen Hinweis eingeben, was Sie mit Merkmalen nicht tun können:

function bar(MyInterface $foo) {
    ...
}

Falls Sie unbedingt wissen müssen, ob eine Klasse ein bestimmtes Merkmal oder eine bestimmte Implementierung verwendet, können Sie der Schnittstelle einfach eine weitere Methode hinzufügen, die je nach Implementierung einen anderen Wert zurückgibt.

  • Dieser Ansatz ist im Falle einer nicht-öffentlichen Logikimplementierung nicht nützlich. Wenn Sie zum Beispiel mit easy verschiedene Verhaltensvarianten erzeugen müssen use BehaviorATrait; use BehaviorBTrait; und es sollte auf einigen geschützten Methodenschnittstellen basieren. Sicherlich ist es kein gutes Design, sondern eine Rede von Werkzeugflexibilität.

    – faules Commit

    25. Dezember 2016 um 8:17 Uhr


  • Die Frage bezieht sich auf Eigenschaften, die keine Schnittstellen implementieren können. Das obige Snippet funktioniert nur für Klassen.

    – Kafoso

    5. April 2017 um 13:13 Uhr


  • Diese Antwort ist in zweierlei Hinsicht falsch. Erstens: Es beantwortet nicht die ursprüngliche Frage, wie man nach verwendeten Merkmalen in Objekten sucht. class_uses macht den Trick. Zu sagen, dass es der “empfohlene Ansatz” ist, eine redundante Schnittstelle für das Merkmal zu erstellen, ist jedoch falsch. Tatsächlich erhöht es die Fehleranfälligkeit, denn wenn Sie vergessen, sowohl Interface als auch Trait zu definieren, kann es zu unerwartetem Verhalten kommen. Und auf der anderen Seite brauchen Sie nicht die Flexibilität, Schnittstelle und Merkmal separat zu definieren. Also mach es einfach!

    – Armin

    29. November 2018 um 14:37 Uhr


  • IMHO stimmt auch etwas mit dem Kommentar von @Armin nicht. Schnittstelle sind stets “redundant”, konstruktionsbedingt. Sie erhöhen jedoch nicht die Fehleranfälligkeit, sondern mindern sie bei richtiger Anwendung, da der Entwickler die Informationen darüber, welche Art von Objekt zu erwarten ist, an einem einzigen Ort aufbewahren kann, während er für verschiedene Implementierungsdetails offen bleibt.

    – JD

    16. Oktober 2019 um 10:38 Uhr


  • Ich stimme @JD zu und würde das hinzufügen (meiner bescheidenen Meinung nach) Traits können für einfache Fälle (fast) sicher ohne Interface verwendet werden. Wenn Sie sich daran gewöhnt haben, Traits zu verwenden und voranzutreiben, werden Sie eines Tages feststellen, dass Sie Schnittstellen benötigen, um implizite Verträge zu garantieren und fehlerhaften, spröden Code zu verhindern. Ja: es ist überflüssig, aber auch die meisten PHP-Klammern sind oder müssen eingegeben werden $this. Schnittstellen stellen sicher, dass Sie (und andere) Ihren Code verstehen und machen Ihren Code SOLID … äh … robuster.

    – Kamafeder

    11. Januar 2021 um 23:41 Uhr

Sie können verwenden class_uses -Funktion, um ein Array aller von einer Klasse verwendeten Eigenschaften zu erhalten.

Dann überprüfen Sie, ob dieses Array einen Schlüssel mit dem gleichen Namen wie das Merkmal enthält, auf das Sie testen.

Wenn ja, dann verwendet Ihre Klasse Ihre Eigenschaft. Wenn nicht, dann wird es nicht verwendet.

  • Hinweis: class_uses sagt Ihnen nur, ob die Klasse das Merkmal direkt implementiert, nicht durch Vererbung. Überprüfen Sie die Kommentare auf php.net auf einige Ideen, wie Sie Ihre eigene Methode entwickeln können, die auch die Vorfahren der Klasse berücksichtigt php.net/manual/en/function.class-uses.php

    – Jeremy Wadhams

    11. Dezember 2018 um 5:13 Uhr

  • Einen Methodenaufruf zu haben und dann in einem Array von Strings nachzuschlagen, um zu verstehen, ob eine Klasse ein Merkmal verwendet oder nicht, ist zu viel Arbeit zur Laufzeit. Es ist viel teurer als die Überprüfung über Sprachkonstrukte wie z instanceof. Ich würde jedem empfehlen, der folgt instanceof FooInterface oben vorgeschlagenen Ansatz, auch wenn er bei nicht-öffentlichen Methoden eine Kehrseite hat.

    – edig

    25. September 2019 um 20:48 Uhr


Es ist nicht wirklich sauber und möglicherweise nicht die richtige Lösung für Ihren Fall. Aber eine Alternative ist zu prüfen, ob das Objekt oder die Klasse eine Methode des Merkmals implementiert (da Sie normalerweise keine vorhandenen Methoden mit Merkmal überschreiben).

if (method_exists($my_object, 'MyTraitSpecificMethod')){
    ...
}

Ich habe gerade herausgefunden, wie Laravel das löst, und dachte, ich würde es hier teilen. Es verwendet class_uses unten, geht aber alle Eltern durch, um alle Merkmale rekursiv zu finden.

Es definiert eine Hilfsfunktion namens class_uses_recursive:

function class_uses_recursive($class)
{
    if (is_object($class)) {
        $class = get_class($class);
    }

    $results = [];

    foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) {
        $results += trait_uses_recursive($class);
    }

    return array_unique($results);
}

function trait_uses_recursive($trait)
{
    $traits = class_uses($trait);

    foreach ($traits as $trait) {
        $traits += trait_uses_recursive($trait);
    }

    return $traits;
}

Und Sie können es so verwenden:

in_array(MyTrait::class, class_uses_recursive($class));

Sie können sehen, wie sie es verwenden, um zu überprüfen, ob ein Modell das Merkmal SoftDeletes implementiert hier:

public function throughParentSoftDeletes()
{
    return in_array(SoftDeletes::class, class_uses_recursive($this->throughParent));
}

Sie können Reflection als zusätzliche Option verwenden.

$reflection = new ReflectionClass($this)

$usedTraits = $reflection->getTraitNames();

if (in_array(MyTrait::class, $usedTraits)) {
    // do Something
}

1179100cookie-checkPHP-Instanz von Traits

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy