Czy php 7 obsługuje podpowiedzi typu dla właściwości klas?
Mam na myśli, nie tylko dla ustawiających / pobierających, ale dla samej właściwości.
Coś jak:
class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}
$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
php
attributes
php-7
type-hinting
variable-types
CarlosCarucce
źródło
źródło
Odpowiedzi:
PHP 7.4 będzie obsługiwać wpisane właściwości, takie jak:
class Person { public string $name; public DateTimeImmutable $dateOfBirth; }
PHP 7.3 i wcześniejsze nie obsługują tego, ale jest kilka alternatyw.
Możesz utworzyć własność prywatną, która jest dostępna tylko poprzez metody pobierające i ustawiające, które mają deklaracje typu:
class Person { private $name; public function getName(): string { return $this->name; } public function setName(string $newName) { $this->name = $newName; } }
Możesz również utworzyć właściwość publiczną i użyć docblock, aby przekazać informacje o typie osobom czytającym kod i używającym IDE, ale nie zapewnia to sprawdzania typu w czasie wykonywania:
class Person { /** * @var string */ public $name; }
I rzeczywiście, możesz łączyć metody pobierające i ustawiające oraz blok dokumentów.
Jeśli jesteś bardziej ryzykowny, można zrobić właściwość fałszywe z
__get
,__set
,__isset
i__unset
magicznych metod i sprawdzić typy siebie. Nie jestem jednak pewien, czy bym to polecił.źródło
array_push($this->foo, $bar)
lubsort($this->foobar)
byłaby wielka sprawa.(new Person())->dateOfBirth = '2001-01-01';
... Pod warunkiem,declare(strict_types=0);
że tak jest. Czy rzuci, czy użyjeDateTimeImmutable
konstruktora? A jeśli tak jest, jaki rodzaj błędu zostanie zgłoszony, jeśli ciąg jest nieprawidłową datą?TypeError
?7.4+:
Dobra wiadomość, że zostanie zaimplementowana w nowych wydaniach, jak wskazał @Andrea. Po prostu zostawię to rozwiązanie na wypadek, gdyby ktoś chciał z niego skorzystać przed 7.4
7,3 lub mniej
Na podstawie powiadomień, które nadal otrzymuję z tego wątku, uważam, że wiele osób miało / ma ten sam problem, co ja. Moim rozwiązaniem w tym przypadku było połączenie seterów +
__set
metody magicznej wewnątrz cechy w celu zasymulowania tego zachowania. Oto ona: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; } } }
A oto demonstracja:
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
Wyjaśnienie
Przede wszystkim zdefiniuj
bar
jako własność prywatną, aby PHP rzutowało__set
automatycznie .__set
sprawdzi, czy w bieżącym obiekcie (method_exists($this, $setter)
) jest zadeklarowany setter . W przeciwnym razie ustawi swoją wartość tak, jak zwykle.Zadeklaruj metodę ustawiającą (setBar), która odbiera argument ze wskazówką o typie (
setBar(Bar $bar)
).Dopóki PHP wykryje, że coś, co nie jest
Bar
instancją, jest przekazywane do ustawiającego, automatycznie wywoła błąd krytyczny: Uncaught TypeError: Argument 1 przekazany do Foo :: setBar () musi być instancją Bar, podana instancja NotBarźródło
Edycja dla PHP 7.4:
Od PHP 7.4 możesz wpisywać atrybuty ( Dokumentacja / Wiki ), co oznacza, że możesz:
class Foo { protected ?Bar $bar; public int $id; ... }
Według wiki wszystkie dopuszczalne wartości to:
PHP <7,4
W rzeczywistości nie jest to możliwe i masz tylko 4 sposoby, aby to faktycznie zasymulować:
Połączyłem je wszystkie tutaj
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; } }
Zwróć uwagę, że w rzeczywistości możesz wpisać zwrot jako? Bar, ponieważ php 7.1 (dopuszcza wartość null), ponieważ może mieć wartość null (niedostępne w php7.0.)
Możesz również wpisać zwrot jako nieważny, ponieważ php7.1
źródło
Możesz użyć settera
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);
Wynik:
TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...
źródło