Do not check variance validity in private methods#2064
Do not check variance validity in private methods#2064ondrejmirtes merged 1 commit intophpstan:1.9.xfrom
Conversation
|
Perfect! :) One more idea - these rules should be more loose for immutable classes. PHPStan supports |
|
I think there's already a similar exception for constructor. |
I can see where the idea is coming from, but I have to disagree. This is not about mutability, it's about substitutability. When I think about generic variance, I do this mental exercise where I "compile" the generics in my head into monomorphized code: a separate class for each instance of the type argument, with the type argument inlined. It really helps me think about the problem in a more hands-on, focused way. interface Animal {}
interface Dog extends Animal {}
/** @template T */
readonly class Collection
{
/**
* @param T $item
* @return Collection<T>
*/
public function add($item): self { /* ... */ }
}I turn the code above into: readonly class CollectionAnimal
{
public function add(Animal $item): self { /* ... */ }
}
readonly class CollectionDog
{
public function add(Dog $item): self { /* ... */ }
}Now, when I make the template type variant, I'm essentially just adding a subtyping relation between the classes. For a covariant template, I'm saying that readonly class CollectionAnimal
{
public function add(Animal $item): self { /* ... */ }
}
readonly class CollectionDog extends CollectionAnimal
{
public function add(Dog $item): self { /* ... */ }
}This obviously violates LSP: wherever I accept
As I understand it, that's a slightly different case. Private members are ignored altogether while in the constructor, the check just omits the initial positional variance. For covariant T: /**
* @param T $a
* @param callable(T): void $b
*/
public function __construct(
mixed $a,
callable $b,
) {}Here, |
|
OK, great, makes sense, thank you :) |
As part of my ongoing work on generics (and type projections, eventually 🤞), I'm investigating an idea to add an (optional) variance check for properties, and I thought it would be a nightmare – you couldn't even have a
class Collectionwith a covariant template for the item type because you couldn't use the template type in the inherently invariantprivate array $items.That's when it occurred to me that perhaps the variance check should ignore private members altogether – variance is all about subtyping, and private members do not really come into play in that context. PHP doesn't enforce variance rules for private members in normal subtyping, and I think neither should PHPStan in generic subtyping.
(Kotlin approves 😉)