101 votes

Indication de type pour les propriétés en PHP 7 ?

Est-ce que php 7 supporte les indications de type pour les propriétés des classes ?

Je veux dire, pas seulement pour régleurs/récupérateurs 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

1 votes

Pas à ma connaissance. Cependant, d'une manière générale cualquier Les contraintes sur la valeur d'une propriété devraient de toute façon être effectuées par un setter. Puisque le setter peut facilement avoir un typehint pour l'argument "value", vous êtes prêt à partir.

0 votes

De nombreux frameworks utilisent protégé attributs (principalement pour les contrôleurs). Pour ces cas en particulier, ce serait très utile.

163voto

Andrea Faulds Points 2041

PHP 7.4 supportera les propriétés typées comme ça :

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

PHP 7.3 et antérieurs ne supportent pas cela, mais il existe des alternatives.

Vous pouvez créer une propriété privée qui n'est accessible que par les getters et setters qui ont des 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 cela ne permet pas de vérifier le type à l'exécution :

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

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

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

0 votes

Ça a l'air vraiment bien. J'ai hâte de voir ce qui va arriver dans les prochaines versions !

0 votes

Un autre problème important est la gestion des références, qui n'interagissent pas vraiment bien avec les déclarations de type et peuvent être désactivées pour ces propriétés. Même sans les problèmes de performance, le fait de ne pas pouvoir faire, disons, array_push($this->foo, $bar) o sort($this->foobar) serait un gros problème.

0 votes

Comment la coercition des types fonctionnera-t-elle ? Par exemple : (new Person())->dateOfBirth = '2001-01-01'; ... Fourni à declare(strict_types=0); c'est-à-dire. Est-ce que ça va lancer ou utiliser le DateTimeImmutable constructeur ? Et si c'est le cas, quel type d'erreur sera lancé si la chaîne est une date invalide ? TypeError ?

13voto

CarlosCarucce Points 1461

7.4+ :

Bonne nouvelle, cela sera implémenté dans les nouvelles versions, comme l'a souligné @Andrea. Je vais simplement laisser cette solution ici au cas où quelqu'un voudrait l'utiliser avant la version 7.4.


7,3 ou moins

D'après les notifications que je reçois encore de ce fil de discussion, je pense que de nombreuses personnes ont eu/ont le même problème que moi. Ma solution pour ce cas était de combiner setters + __set méthode magique à 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 une propriété privée. PHP va donc lancer __set automatiquement .

__set vérifiera s'il existe un setter déclaré dans l'objet courant ( method_exists($this, $setter) ). Sinon, il ne fera que fixer sa valeur comme il le ferait normalement.

Déclarez une méthode setter (setBar) qui reçoit un argument de type hinted ( setBar(Bar $bar) ).

Tant que PHP détecte que quelque chose qui n'est pas Bar est passée au setter, cela déclenchera automatiquement une erreur fatale : Erreur de Type Non Rattrapée : Argument 1 passé à Foo::setBar() doit être une instance de Bar, instance de NotBar donnée

10voto

Bruno Guignard Points 11

Modifier pour PHP 7.4 :

Depuis PHP 7.4, vous pouvez saisir des attributs ( Documentation / Wiki ), ce qui signifie que vous pouvez faire :

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

Selon wiki, toutes les valeurs acceptables sont :

  • bool, int, float, string, array, object
  • itérable
  • soi, parent
  • tout nom de classe ou d'interface
  • ?type // où "type" peut être l'un des éléments ci-dessus

PHP < 7.4

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

  • Valeurs par défaut
  • Décorateurs dans les 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 en fait taper le retour comme ?Bar depuis php 7.1 (nullable) parce qu'il pourrait être nul (non disponible en php7.0).

Vous pouvez également saisir le retour comme void depuis php7.1.

1voto

Richard Points 921

Vous pouvez utiliser le 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 ...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X