Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This makes it possible that a new instance of a class-string will be returned. ```php /** * @var array<string, class-string> */ private const TYPES = [ 'foo' => DateTime::class, 'bar' => DateTimeImmutable::class, ]; /** * @template T of key-of<self::TYPES> * @param T $type * * @return new<self::TYPES[T]> */ public static function get(string $type) : ?object { $class = self::TYPES[$type]; return new $class('now'); } ``` See phpstan/phpstan#9704 The work was done by @rvanvelzen in a gist. I just created the PR for it. Co-Authored-By: Richard van Velzen <rvanvelzen1@gmail.com>
- Loading branch information
1 parent
1090835
commit 1f95482
Showing
4 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type; | ||
|
||
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; | ||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; | ||
use PHPStan\PhpDocParser\Ast\Type\TypeNode; | ||
use PHPStan\Type\Generic\TemplateTypeVariance; | ||
use PHPStan\Type\Traits\LateResolvableTypeTrait; | ||
use PHPStan\Type\Traits\NonGeneralizableTypeTrait; | ||
use function sprintf; | ||
|
||
/** @api */ | ||
class NewObjectType implements CompoundType, LateResolvableType | ||
{ | ||
|
||
use LateResolvableTypeTrait; | ||
use NonGeneralizableTypeTrait; | ||
|
||
public function __construct(private Type $type) | ||
{ | ||
} | ||
|
||
public function getType(): Type | ||
{ | ||
return $this->type; | ||
} | ||
|
||
public function getReferencedClasses(): array | ||
{ | ||
return $this->type->getReferencedClasses(); | ||
} | ||
|
||
public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array | ||
{ | ||
return $this->type->getReferencedTemplateTypes($positionVariance); | ||
} | ||
|
||
public function equals(Type $type): bool | ||
{ | ||
return $type instanceof self | ||
&& $this->type->equals($type->type); | ||
} | ||
|
||
public function describe(VerbosityLevel $level): string | ||
{ | ||
return sprintf('new<%s>', $this->type->describe($level)); | ||
} | ||
|
||
public function isResolvable(): bool | ||
{ | ||
return !TypeUtils::containsTemplateType($this->type); | ||
} | ||
|
||
protected function getResult(): Type | ||
{ | ||
return $this->type->getObjectTypeOrClassStringObjectType(); | ||
} | ||
|
||
/** | ||
* @param callable(Type): Type $cb | ||
*/ | ||
public function traverse(callable $cb): Type | ||
{ | ||
$type = $cb($this->type); | ||
|
||
if ($this->type === $type) { | ||
return $this; | ||
} | ||
|
||
return new self($type); | ||
} | ||
|
||
public function traverseSimultaneously(Type $right, callable $cb): Type | ||
{ | ||
if (!$right instanceof self) { | ||
return $this; | ||
} | ||
|
||
$type = $cb($this->type, $right->type); | ||
|
||
if ($this->type === $type) { | ||
return $this; | ||
} | ||
|
||
return new self($type); | ||
} | ||
|
||
public function toPhpDocNode(): TypeNode | ||
{ | ||
return new GenericTypeNode(new IdentifierTypeNode('new'), [$this->type->toPhpDocNode()]); | ||
} | ||
|
||
/** | ||
* @param mixed[] $properties | ||
*/ | ||
public static function __set_state(array $properties): Type | ||
{ | ||
return new self( | ||
$properties['type'], | ||
); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
namespace Bug9704; | ||
|
||
use DateTime; | ||
use DateTimeImmutable; | ||
use function PHPStan\dumpType; | ||
use function PHPStan\Testing\assertType; | ||
|
||
class Foo | ||
{ | ||
/** | ||
* @var array<string, class-string> | ||
*/ | ||
private const TYPES = [ | ||
'foo' => DateTime::class, | ||
'bar' => DateTimeImmutable::class, | ||
]; | ||
|
||
/** | ||
* @template M of self::TYPES | ||
* @template T of key-of<M> | ||
* @param T $type | ||
* | ||
* @return new<M[T]> | ||
*/ | ||
public static function get(string $type) : object | ||
{ | ||
$class = self::TYPES[$type]; | ||
|
||
return new $class('now'); | ||
} | ||
|
||
/** | ||
* @template T of key-of<self::TYPES> | ||
* @param T $type | ||
* | ||
* @return new<self::TYPES[T]> | ||
*/ | ||
public static function get2(string $type) : object | ||
{ | ||
$class = self::TYPES[$type]; | ||
|
||
return new $class('now'); | ||
} | ||
} | ||
|
||
assertType(DateTime::class, Foo::get('foo')); | ||
assertType(DateTimeImmutable::class, Foo::get('bar')); | ||
|
||
assertType(DateTime::class, Foo::get2('foo')); | ||
assertType(DateTimeImmutable::class, Foo::get2('bar')); | ||
|
||
|