web-dev-qa-db-fra.com

Repères de type pour les propriétés dans PHP 7?

Est-ce que php 7 prend en charge les indicateurs de type pour les propriétés de classe? 

Je veux dire, pas seulement pour setters/getters mais pour la propriété elle-même. 

Quelque chose comme:

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

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

PHP 7.4 supportera les propriétés typées like so:

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

PHP 7.3 et les versions antérieures ne supportent pas cela, mais il existe quelques alternatives.

Vous pouvez créer une propriété privée accessible uniquement par le biais de getters et de setters dotés de déclarations de type:

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

Vous pouvez également créer une propriété publique et utiliser un docblock pour fournir des informations sur le type aux personnes qui lisent le code et utilisent un IDE, mais ceci ne fournit aucune vérification de type à l'exécution:

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

Et en effet, vous pouvez combiner des getters et des setters et un docblock.

Si vous êtes plus aventureux, vous pouvez créer une fausse propriété avec les méthodes magiques __get, __set, __isset ET __unset , et vérifier les types vous-même. Je ne suis cependant pas sûr de le recommander.

79
Andrea

7.4+:

Bonne nouvelle qu’elle sera mise en œuvre dans les nouvelles versions, comme @Andrea l’a souligné… Je vais laisser cette solution ici au cas où quelqu'un voudrait l'utiliser avant la 7.4


7.3 ou moins

Sur la base des notifications que je reçois toujours de ce fil, je pense que beaucoup de gens ont eu/ont le même problème que moi. Ma solution pour ce cas était de combiner la méthode magique setters + __set à l’intérieur d’un trait afin de simuler ce comportement . Le voici:

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;
        }
    }
}

Et voici la démonstration:

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

Explication

Tout d’abord, définissez bar comme propriété privée afin que PHP transforme __setautomagically.

__set vérifiera s'il y a un installateur déclaré dans l'objet actuel (method_exists($this, $setter)). Sinon, il ne fera que définir sa valeur comme il le ferait normalement.

Déclarez une méthode de définition (setBar) qui reçoit un argument à indice de type (setBar(Bar $bar)).

Tant que PHP détecte que quelque chose qui n'est pas une instance Bar est passée au poseur, cela provoquera automatiquement une erreur fatale: Uncaught TypeError: L'argument 1 passé à Foo :: setBar () doit être une instance of Bar, exemple de NotBar donné

2
CarlosCarucce

En fait, ce n'est pas possible et vous n'avez que 4 façons de le simuler:

  • Les valeurs par défaut
  • Décorateurs en blocs de commentaires
  • Valeurs par défaut dans le constructeur
  • Getters et setters

Je les ai tous réunis ici

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;
    }
}

Notez que vous pouvez réellement taper le retour comme? Bar depuis PHP 7.1 (nullable) car il pourrait être nul (non disponible dans php7.0)

Vous pouvez aussi taper le retour comme nul depuis php7.1

1
Bruno Guignard

Vous pouvez utiliser setter

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);

Sortie:

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