Tipp für Eigenschaften in PHP 7 eingeben?

80

Unterstützt PHP 7 Typhinweise für Klasseneigenschaften?

Ich meine, nicht nur für Setter / Getter, sondern für die Immobilie selbst.

Etwas wie:

class Foo {
    /**
     *
     * @var Bar
     */
    public $bar : Bar;
}

$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
CarlosCarucce
quelle
1
Nicht dass ich wüsste. Im Allgemeinen jedoch keine Einschränkungen für den Wert einer Eigenschaft sollte auf jeden Fall durch einen Setter erfolgen. Da der Setter leicht einen Tipp für das Argument "value" haben kann, können Sie loslegen.
Niet the Dark Absol
Viele Frameworks verwenden geschützte Attribute (hauptsächlich für Controller). Insbesondere in diesen Fällen wäre dies sehr nützlich.
CarlosCarucce

Antworten:

134

PHP 7.4 unterstützt typisierte Eigenschaften wie folgt :

class Person
{
    public string $name;
    public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 und frühere Versionen unterstützen dies nicht, es gibt jedoch einige Alternativen.

Sie können eine private Eigenschaft erstellen, auf die nur über Getter und Setter mit Typdeklarationen zugegriffen werden kann:

class Person
{
    private $name;
    public function getName(): string {
        return $this->name;
    }
    public function setName(string $newName) {
        $this->name = $newName;
    }
}

Sie können auch eine öffentliche Eigenschaft erstellen und einen Dokumentblock verwenden, um Typinformationen für Personen bereitzustellen, die den Code lesen und eine IDE verwenden. Dies bietet jedoch keine Laufzeit-Typprüfung:

class Person
{
    /**
      * @var string
      */
    public $name;
}

In der Tat können Sie Getter und Setter und einen Docblock kombinieren.

Wenn Sie mehr Abenteuer sind, können Sie ein gefälschtes Grundstück mit dem machen __get, __set, __issetund __unsetmagischen Methoden , und überprüfen Sie die Arten selbst. Ich bin mir nicht sicher, ob ich es empfehlen würde.

Andrea
quelle
Hört sich wirklich gut an. Ich kann es kaum erwarten zu sehen, was bei den nächsten Veröffentlichungen kommt!
CarlosCarucce
Ein weiteres wichtiges Problem ist die Behandlung von Referenzen, die nicht wirklich gut mit Typdeklarationen interagieren und für solche Eigenschaften möglicherweise deaktiviert werden müssen. Auch ohne die Leistungsprobleme wäre es eine große Sache , nicht in der Lage zu sein, zu sagen array_push($this->foo, $bar)oder sort($this->foobar)zu sagen .
Andrea
Wie wird der Zwang von Typen funktionieren? Zum Beispiel: (new Person())->dateOfBirth = '2001-01-01';... Vorausgesetzt declare(strict_types=0);, das ist. Wird es den DateTimeImmutableKonstruktor werfen oder benutzen ? Und wenn dies der Fall ist, welche Art von Fehler wird ausgelöst, wenn die Zeichenfolge ein ungültiges Datum ist? TypeError?
Lmerino
@Imerino Es gibt keine implizite Konvertierung zu DateTime (unveränderlich) und war es noch nie
Andrea
12

7.4+:

Gute Nachrichten, dass es in den neuen Versionen implementiert wird, wie @Andrea betonte. Ich werde diese Lösung nur hier belassen, falls jemand sie vor 7.4 verwenden möchte


7,3 oder weniger

Aufgrund der Benachrichtigungen, die ich immer noch von diesem Thread erhalte, glaube ich, dass viele Leute da draußen das gleiche Problem hatten / haben wie ich. Meine Lösung für diesen Fall bestand darin, Setter + __setmagische Methode in einem Merkmal zu kombinieren , um dieses Verhalten zu simulieren. Hier ist es:

trait SettersTrait
{
    /**
     * @param $name
     * @param $value
     */
    public function __set($name, $value)
    {
        $setter = 'set'.$name;
        if (method_exists($this, $setter)) {
            $this->$setter($value);
        } else {
            $this->$name = $value;
        }
    }
}

Und hier ist die Demonstration:

class Bar {}
class NotBar {}

class Foo
{
    use SettersTrait; //It could be implemented within this class but I used it as a trait for more flexibility

    /**
     *
     * @var Bar
     */
    private $bar;

    /**
     * @param Bar $bar
     */
    protected function setBar(Bar $bar)
    {
        //(optional) Protected so it wont be called directly by external 'entities'
        $this->bar = $bar;
    }
}

$foo = new Foo();
$foo->bar = new NotBar(); //Error
//$foo->bar = new Bar(); //Success

Erläuterung

Definieren Sie zunächst barals Privateigentum, damit PHP __set automatisch umgewandelt wird .

__setprüft, ob im aktuellen Objekt ( method_exists($this, $setter)) ein Setter deklariert ist . Andernfalls wird der Wert nur wie gewohnt eingestellt.

Deklarieren Sie eine Setter-Methode (setBar), die ein typbezogenes Argument ( setBar(Bar $bar)) empfängt .

Solange PHP erkennt, dass etwas, das keine BarInstanz ist, an den Setter übergeben wird, löst es automatisch einen schwerwiegenden Fehler aus: Nicht erfasster Typ Fehler: Argument 1, das an Foo :: setBar () übergeben wird, muss eine Instanz von Bar sein, Instanz von NotBar angegeben

CarlosCarucce
quelle
4

Bearbeiten für PHP 7.4:

Seit PHP 7.4 können Sie Attribute ( Dokumentation / Wiki ) eingeben, was bedeutet, dass Sie Folgendes tun können:

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}

Laut Wiki sind alle akzeptablen Werte:

  • bool, int, float, string, array, object
  • wiederholbar
  • Selbst, Eltern
  • Beliebiger Klassen- oder Schnittstellenname
  • ? type // wobei "type" eine der oben genannten sein kann

PHP <7,4

Es ist eigentlich nicht möglich und Sie haben nur 4 Möglichkeiten, es tatsächlich zu simulieren:

  • Standardwerte
  • Dekorateure in Kommentarblöcken
  • Standardwerte im Konstruktor
  • Getter und Setter

Ich habe sie alle hier kombiniert

class Foo
{
    /**
     * @var Bar
     */
    protected $bar = null;

    /** 
    * Foo constructor
    * @param Bar $bar
    **/
    public function __construct(Bar $bar = null){
        $this->bar = $bar;
    }
    
    /**
    * @return Bar
    */
    public function getBar() : ?Bar{
        return $this->bar;
    }

    /**
    * @param Bar $bar
    */
    public function setBar(Bar $bar) {
        $this->bar = $bar;
    }
}

Beachten Sie, dass Sie die Rückgabe tatsächlich als? Bar seit PHP 7.1 (nullbar) eingeben können, da sie null sein könnte (in PHP7.0 nicht verfügbar).

Sie können die Rückgabe auch als ungültig seit php7.1 eingeben

Bruno Guignard
quelle
1

Sie können Setter verwenden

class Bar {
    public $val;
}

class Foo {
    /**
     *
     * @var Bar
     */
    private $bar;

    /**
     * @return Bar
     */
    public function getBar()
    {
        return $this->bar;
    }

    /**
     * @param Bar $bar
     */
    public function setBar(Bar $bar)
    {
        $this->bar = $bar;
    }

}

$fooInstance = new Foo();
// $fooInstance->bar = new NotBar(); //Error
$fooInstance->setBar($fooInstance);

Ausgabe:

TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...
Richard
quelle