mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-31 16:30:51 +00:00
055cd31f66
2ff0f08bd7
[PHP 8.0] Add always class to AnnotationToAttribute to include string to ::class reference conversion (#5619)
139 lines
5.3 KiB
PHP
139 lines
5.3 KiB
PHP
<?php
|
|
|
|
declare (strict_types=1);
|
|
namespace Rector\Doctrine\NodeManipulator;
|
|
|
|
use PhpParser\Node\Scalar\String_;
|
|
use PhpParser\Node\Stmt\Property;
|
|
use PHPStan\Type\BooleanType;
|
|
use PHPStan\Type\FloatType;
|
|
use PHPStan\Type\IntegerType;
|
|
use PHPStan\Type\MixedType;
|
|
use PHPStan\Type\NullType;
|
|
use PHPStan\Type\ObjectType;
|
|
use PHPStan\Type\StringType;
|
|
use PHPStan\Type\Type;
|
|
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
|
|
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
|
use Rector\BetterPhpDocParser\PhpDoc\StringNode;
|
|
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
|
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
|
use Rector\Doctrine\Enum\MappingClass;
|
|
use Rector\Doctrine\NodeAnalyzer\AttributeFinder;
|
|
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
|
final class ColumnPropertyTypeResolver
|
|
{
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
|
|
*/
|
|
private $phpDocInfoFactory;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\NodeTypeResolver\PHPStan\Type\TypeFactory
|
|
*/
|
|
private $typeFactory;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Doctrine\NodeAnalyzer\AttributeFinder
|
|
*/
|
|
private $attributeFinder;
|
|
/**
|
|
* @var array<string, Type>
|
|
* @readonly
|
|
*/
|
|
private $doctrineTypeToScalarType;
|
|
/**
|
|
* @var string
|
|
*/
|
|
private const DATE_TIME_INTERFACE = 'DateTimeInterface';
|
|
/**
|
|
* @param array<string, Type> $doctrineTypeToScalarType
|
|
* @see https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/basic-mapping.html#doctrine-mapping-types
|
|
*/
|
|
public function __construct(PhpDocInfoFactory $phpDocInfoFactory, TypeFactory $typeFactory, AttributeFinder $attributeFinder, array $doctrineTypeToScalarType = null)
|
|
{
|
|
$doctrineTypeToScalarType = $doctrineTypeToScalarType ?? [
|
|
'tinyint' => new BooleanType(),
|
|
'boolean' => new BooleanType(),
|
|
// integers
|
|
'smallint' => new IntegerType(),
|
|
'mediumint' => new IntegerType(),
|
|
'int' => new IntegerType(),
|
|
'integer' => new IntegerType(),
|
|
'numeric' => new IntegerType(),
|
|
// floats
|
|
'float' => new FloatType(),
|
|
'double' => new FloatType(),
|
|
'real' => new FloatType(),
|
|
// strings
|
|
'decimal' => new StringType(),
|
|
'bigint' => new StringType(),
|
|
'tinytext' => new StringType(),
|
|
'mediumtext' => new StringType(),
|
|
'longtext' => new StringType(),
|
|
'text' => new StringType(),
|
|
'varchar' => new StringType(),
|
|
'string' => new StringType(),
|
|
'char' => new StringType(),
|
|
'longblob' => new StringType(),
|
|
'blob' => new StringType(),
|
|
'mediumblob' => new StringType(),
|
|
'tinyblob' => new StringType(),
|
|
'binary' => new StringType(),
|
|
'varbinary' => new StringType(),
|
|
'set' => new StringType(),
|
|
// date time objects
|
|
'date' => new ObjectType(self::DATE_TIME_INTERFACE),
|
|
'datetime' => new ObjectType(self::DATE_TIME_INTERFACE),
|
|
'timestamp' => new ObjectType(self::DATE_TIME_INTERFACE),
|
|
'time' => new ObjectType(self::DATE_TIME_INTERFACE),
|
|
'year' => new ObjectType(self::DATE_TIME_INTERFACE),
|
|
];
|
|
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
|
$this->typeFactory = $typeFactory;
|
|
$this->attributeFinder = $attributeFinder;
|
|
$this->doctrineTypeToScalarType = $doctrineTypeToScalarType;
|
|
}
|
|
public function resolve(Property $property, bool $isNullable) : ?Type
|
|
{
|
|
$expr = $this->attributeFinder->findAttributeByClassArgByName($property, MappingClass::COLUMN, 'type');
|
|
if ($expr instanceof String_) {
|
|
return $this->createPHPStanTypeFromDoctrineStringType($expr->value, $isNullable);
|
|
}
|
|
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
|
|
return $this->resolveFromPhpDocInfo($phpDocInfo, $isNullable);
|
|
}
|
|
private function resolveFromPhpDocInfo(PhpDocInfo $phpDocInfo, bool $isNullable) : ?\PHPStan\Type\Type
|
|
{
|
|
$doctrineAnnotationTagValueNode = $phpDocInfo->findOneByAnnotationClass(MappingClass::COLUMN);
|
|
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
|
return null;
|
|
}
|
|
$typeArrayItemNode = $doctrineAnnotationTagValueNode->getValue('type');
|
|
if (!$typeArrayItemNode instanceof ArrayItemNode) {
|
|
return new MixedType();
|
|
}
|
|
$typeValue = $typeArrayItemNode->value;
|
|
if ($typeValue instanceof StringNode) {
|
|
$typeValue = $typeValue->value;
|
|
}
|
|
if (!\is_string($typeValue)) {
|
|
return null;
|
|
}
|
|
return $this->createPHPStanTypeFromDoctrineStringType($typeValue, $isNullable);
|
|
}
|
|
private function createPHPStanTypeFromDoctrineStringType(string $type, bool $isNullable) : Type
|
|
{
|
|
$scalarType = $this->doctrineTypeToScalarType[$type] ?? null;
|
|
if (!$scalarType instanceof Type) {
|
|
return new MixedType();
|
|
}
|
|
$types = [$scalarType];
|
|
if ($isNullable) {
|
|
$types[] = new NullType();
|
|
}
|
|
return $this->typeFactory->createMixedPassedOrUnionType($types);
|
|
}
|
|
}
|