|
| 1 | +<?php declare(strict_types = 1); |
| 2 | + |
| 3 | +namespace Bug5298; |
| 4 | + |
| 5 | +interface WorldProvider{} |
| 6 | + |
| 7 | +interface WritableWorldProvider extends WorldProvider{} |
| 8 | + |
| 9 | +/** |
| 10 | + * @phpstan-template TWorldProvider of WorldProvider |
| 11 | + * @phpstan-type IsValid \Closure(string $path) : bool |
| 12 | + * @phpstan-type FromPath \Closure(string $path) : TWorldProvider |
| 13 | + */ |
| 14 | +class WorldProviderManagerEntry{ |
| 15 | + /** @phpstan-param TWorldProvider $provider */ |
| 16 | + public function acceptsTWorldProvider(WorldProvider $provider) : void{} |
| 17 | +} |
| 18 | + |
| 19 | + |
| 20 | +final class WorldProviderManager{ |
| 21 | + /** |
| 22 | + * @var WorldProviderManagerEntry[] |
| 23 | + * @phpstan-var array<string, WorldProviderManagerEntry<WorldProvider>> |
| 24 | + */ |
| 25 | + protected $providers = []; |
| 26 | + |
| 27 | + /** |
| 28 | + * @phpstan-template T of WorldProvider |
| 29 | + * @phpstan-param WorldProviderManagerEntry<T> $providerEntry |
| 30 | + */ |
| 31 | + public function addProvider(WorldProviderManagerEntry $providerEntry, string $name, bool $overwrite = false) : void{ |
| 32 | + $name = strtolower($name); |
| 33 | + if(!$overwrite and isset($this->providers[$name])){ |
| 34 | + throw new \InvalidArgumentException("Alias \"$name\" is already assigned"); |
| 35 | + } |
| 36 | + |
| 37 | + $this->providers[$name] = $providerEntry; //should be an error, T of WorldProvider is not invariant with WorldProvider |
| 38 | + \PHPStan\dumpType($providerEntry); |
| 39 | + \PHPStan\dumpType($this->providers); |
| 40 | + } |
| 41 | + |
| 42 | + public function doSomething(string $name, WorldProvider $provider) : void{ |
| 43 | + $this->providers[$name]->acceptsTWorldProvider($provider); //error, WorldProvider might not be a subclass of the template bound |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +$p = new WorldProviderManager(); |
| 48 | +/** @phpstan-var WorldProviderManagerEntry<WritableWorldProvider> */ |
| 49 | +$entry = new WorldProviderManagerEntry(); //acceptsTWorldProvider() doesn't accept WorldProvider |
| 50 | +$p->addProvider($entry, "test"); |
| 51 | +$p->doSomething("test", new class implements WorldProvider{}); //bang |
0 commit comments