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
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.
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 __set
automagically.
__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é
En fait, ce n'est pas possible et vous n'avez que 4 façons de le simuler:
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
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 ...