Есть у меня одна черта: я периодически возвращаюсь ко всем своим костылям, пока не сделаю нормально
Раунд 3
final class A extends ArrayObject
{
public function self() { return fn (): self => new self(); }
public function parent() { return fn (): parent => new parent(); }
public function static() { return fn (): static => new static(); }
}
final class B extends stdClass {}
$self = (new A())->self();
echo $self()::class; // A
echo $self->call(new B)::class; // B
$parent = (new A())->parent();
echo $parent()::class; // ArrayObject
echo $parent->call(new B)::class; // stdClass
$static = (new A())->static();
echo $static()::class; // A
echo $static->call(new B)::class; // B
Получается, что
self, parent и static отлично себя чувствуют в анонимных функциях и, как и в трейтах, резолвятся в зависимости от скоупа, в том числе поддерживают его изменение в рантайме. Посмотрев на этот сниппет, мы наконец-то согласились, что относительные типы класса везде в PHP ведут себя логично и единообразно и должны быть замоделированы как first-class типы.Теперь в typhoon/type 0.4 можно создавать
self, parent и static с классом скоупа и без него. Последнее как раз требуется в трейтах и непривязанных анонимных функциях. В TypeVisitor каждый относительный тип обрабатывается индивидуально. Ну и, конечно, поддерживаются аргументы типов (а-ля self<X, Y>). Комбо-пример:
trait T
{
/**
* @return array{self, parent, static}
*/
public function types(): array
{
return [$this, $this, $this];
}
}
abstract class A extends stdClass
{
use T;
}
final class B extends A {}
$type = TyphoonReflector::build()
->reflectClass(B::class)
->methods()['types']
->returnType();
echo stringify($type);
// array{self@A, parent@stdClass, static@B}