From 0589566b1b8c9258b2129f213fa21d986fedcd56 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 14 Jan 2020 13:18:30 +0100 Subject: [PATCH 1/3] cleanup --- rector.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/rector.yaml b/rector.yaml index e92676364d64..c57bd88cdb91 100644 --- a/rector.yaml +++ b/rector.yaml @@ -1,7 +1,5 @@ services: Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector: null -# Rector\SOLID\Rector\If_\ChangeNestedIfsToEarlyReturnRector: null -# Rector\SOLID\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector: null imports: - { resource: "create-rector.yaml", ignore_errors: true } From e64f787cde2ed10b8fc99789a756592b6b82f0b8 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 14 Jan 2020 14:09:38 +0100 Subject: [PATCH 2/3] [PHPStanStaticTypeMapper] Decouple new services to handle PHPStan type mapping --- composer.json | 1 + ecs.yaml | 3 + .../src/Finder/PHPStanTypeClassFinder.php | 60 +++ .../NodeTypeResolver/src/StaticTypeMapper.php | 366 +-------------- .../config/config.yaml | 8 + .../src/PHPStanStaticTypeMapper.php | 420 ++++++++++++++++++ 6 files changed, 503 insertions(+), 355 deletions(-) create mode 100644 packages/NodeTypeResolver/src/Finder/PHPStanTypeClassFinder.php create mode 100644 packages/PHPStanStaticTypeMapper/config/config.yaml create mode 100644 packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php diff --git a/composer.json b/composer.json index d64945ef2f55..631fd6ec1af0 100644 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ "Rector\\Nette\\": "packages/Nette/src", "Rector\\NodeTypeResolver\\": "packages/NodeTypeResolver/src", "Rector\\PHPStan\\": "packages/PHPStan/src", + "Rector\\PHPStanStaticTypeMapper\\": "packages/PHPStanStaticTypeMapper/src", "Rector\\PHPUnitSymfony\\": "packages/PHPUnitSymfony/src", "Rector\\PHPUnit\\": "packages/PHPUnit/src", "Rector\\PSR4\\": "packages/PSR4/src", diff --git a/ecs.yaml b/ecs.yaml index 5184434ede6e..8d2df6b19a93 100644 --- a/ecs.yaml +++ b/ecs.yaml @@ -93,7 +93,10 @@ parameters: - 'packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/FqnNamePhpDocNodeDecorator.php' - 'packages/NodeTypeResolver/src/PHPStan/Type/StaticTypeAnalyzer.php' - 'src/NodeContainer/ParsedNodesByType.php' + + - 'packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php' - 'packages/NodeTypeResolver/src/StaticTypeMapper.php' + - 'packages/PHPStan/src/Rector/Node/RemoveNonExistingVarAnnotationRector.php' - 'packages/Architecture/src/Rector/Class_/ConstructorInjectionToActionInjectionRector.php' - 'src/PhpParser/Node/Commander/NodeRemovingCommander.php' diff --git a/packages/NodeTypeResolver/src/Finder/PHPStanTypeClassFinder.php b/packages/NodeTypeResolver/src/Finder/PHPStanTypeClassFinder.php new file mode 100644 index 000000000000..3b5ebb26b12f --- /dev/null +++ b/packages/NodeTypeResolver/src/Finder/PHPStanTypeClassFinder.php @@ -0,0 +1,60 @@ +addDirectory($this->getPhpstanPharSrcTypeDirectoryPath()); + + $robotLoader->setTempDirectory(sys_get_temp_dir() . '/_phpstan_types'); + $robotLoader->acceptFiles = ['*Type.php']; + $robotLoader->rebuild(); + + $classLikesToFilePaths = $robotLoader->getIndexedClasses(); + $classLikes = array_keys($classLikesToFilePaths); + + return $this->filterClassesOnly($classLikes); + } + + /** + * @param string[] $classLikes + * @return string[] + */ + private function filterClassesOnly(array $classLikes): array + { + $classes = []; + foreach ($classLikes as $classLike) { + if (! class_exists($classLike)) { + continue; + } + + if (Strings::match($classLike, '#\\\\Accessory\\\\#')) { + continue; + } + + $classes[] = $classLike; + } + return $classes; + } + + /** + * @see https://github.com/dg/nette-robot-loader/blob/593c0e40e511c0b0700610a6a3964a210219139f/tests/Loaders/RobotLoader.phar.phpt#L33 + */ + private function getPhpstanPharSrcTypeDirectoryPath(): string + { + $phpstanPharRealpath = realpath(__DIR__ . '/../../../../vendor/phpstan/phpstan/phpstan.phar'); + + return 'phar://' . $phpstanPharRealpath . '/src/Type'; + } +} diff --git a/packages/NodeTypeResolver/src/StaticTypeMapper.php b/packages/NodeTypeResolver/src/StaticTypeMapper.php index bd984b995116..54ffee62ccc7 100644 --- a/packages/NodeTypeResolver/src/StaticTypeMapper.php +++ b/packages/NodeTypeResolver/src/StaticTypeMapper.php @@ -49,7 +49,6 @@ use PHPStan\Type\TypeWithClassName; use PHPStan\Type\UnionType; use PHPStan\Type\VoidType; -use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode; use Rector\BetterPhpDocParser\Type\PreSlashStringType; use Rector\Exception\NotImplementedException; use Rector\Exception\ShouldNotHappenException; @@ -61,9 +60,8 @@ use Rector\PHPStan\Type\ParentStaticType; use Rector\PHPStan\Type\SelfObjectType; use Rector\PHPStan\Type\ShortenedObjectType; +use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper; use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier; -use Rector\ValueObject\PhpVersionFeature; -use Traversable; /** * Maps PhpParser <=> PHPStan <=> PHPStan doc <=> string type nodes to between all possible formats @@ -85,76 +83,26 @@ final class StaticTypeMapper */ private $objectTypeSpecifier; + /** + * @var PHPStanStaticTypeMapper + */ + private $phpStanStaticTypeMapper; + public function __construct( PhpVersionProvider $phpVersionProvider, TypeFactory $typeFactory, - ObjectTypeSpecifier $objectTypeSpecifier + ObjectTypeSpecifier $objectTypeSpecifier, + PHPStanStaticTypeMapper $phpStanStaticTypeMapper ) { $this->phpVersionProvider = $phpVersionProvider; $this->typeFactory = $typeFactory; $this->objectTypeSpecifier = $objectTypeSpecifier; + $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; } public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode { - if ($phpStanType instanceof MixedType) { - return new IdentifierTypeNode('mixed'); - } - - if ($phpStanType instanceof UnionType) { - $unionTypesNodes = []; - foreach ($phpStanType->getTypes() as $unionedType) { - $unionTypesNodes[] = $this->mapPHPStanTypeToPHPStanPhpDocTypeNode($unionedType); - } - - $unionTypesNodes = array_unique($unionTypesNodes); - - return new AttributeAwareUnionTypeNode($unionTypesNodes); - } - - if ($phpStanType instanceof ArrayType || $phpStanType instanceof IterableType) { - $itemTypeNode = $this->mapPHPStanTypeToPHPStanPhpDocTypeNode($phpStanType->getItemType()); - - if ($itemTypeNode instanceof UnionTypeNode) { - return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode); - } - - return new ArrayTypeNode($itemTypeNode); - } - - if ($phpStanType instanceof IntegerType) { - return new IdentifierTypeNode('int'); - } - - if ($phpStanType instanceof ClassStringType) { - return new IdentifierTypeNode('class-string'); - } - - if ($phpStanType instanceof StringType) { - return new IdentifierTypeNode('string'); - } - - if ($phpStanType instanceof BooleanType) { - return new IdentifierTypeNode('bool'); - } - - if ($phpStanType instanceof FloatType) { - return new IdentifierTypeNode('float'); - } - - if ($phpStanType instanceof ObjectType) { - return new IdentifierTypeNode('\\' . $phpStanType->getClassName()); - } - - if ($phpStanType instanceof NullType) { - return new IdentifierTypeNode('null'); - } - - if ($phpStanType instanceof NeverType) { - return new IdentifierTypeNode('mixed'); - } - - throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + return $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($phpStanType); } /** @@ -162,160 +110,7 @@ public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNo */ public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, ?string $kind = null): ?Node { - if ($phpStanType instanceof VoidType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::VOID_TYPE)) { - if (in_array($kind, ['param', 'property'], true)) { - // param cannot be void - return null; - } - - return new Identifier('void'); - } - - return null; - } - - if ($phpStanType instanceof SelfObjectType) { - return new Identifier('self'); - } - - if ($phpStanType instanceof IntegerType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { - return new Identifier('int'); - } - - return null; - } - - if ($phpStanType instanceof StringType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { - return new Identifier('string'); - } - - return null; - } - - if ($phpStanType instanceof BooleanType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { - return new Identifier('bool'); - } - - return null; - } - - if ($phpStanType instanceof FloatType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { - return new Identifier('float'); - } - - return null; - } - - if ($phpStanType instanceof ArrayType) { - return new Identifier('array'); - } - - if ($phpStanType instanceof IterableType) { - return new Identifier('iterable'); - } - - if ($phpStanType instanceof ThisType) { - return new Identifier('self'); - } - - if ($phpStanType instanceof ParentStaticType) { - return new Identifier('parent'); - } - - if ($phpStanType instanceof StaticType) { - return null; - } - - if ($phpStanType instanceof CallableType || $phpStanType instanceof ClosureType) { - if ($kind === 'property') { - return null; - } - - return new Identifier('callable'); - } - - if ($phpStanType instanceof ShortenedObjectType) { - return new FullyQualified($phpStanType->getFullyQualifiedName()); - } - - if ($phpStanType instanceof AliasedObjectType) { - return new Name($phpStanType->getClassName()); - } - - if ($phpStanType instanceof TypeWithClassName) { - $lowerCasedClassName = strtolower($phpStanType->getClassName()); - if ($lowerCasedClassName === 'callable') { - return new Identifier('callable'); - } - - if ($lowerCasedClassName === 'self') { - return new Identifier('self'); - } - - if ($lowerCasedClassName === 'static') { - return null; - } - - if ($lowerCasedClassName === 'mixed') { - return null; - } - - return new FullyQualified($phpStanType->getClassName()); - } - - if ($phpStanType instanceof UnionType) { - // match array types - $arrayNode = $this->matchArrayTypes($phpStanType); - if ($arrayNode !== null) { - return $arrayNode; - } - - // special case for nullable - $nullabledType = $this->matchTypeForNullableUnionType($phpStanType); - if ($nullabledType === null) { - // use first unioned type in case of unioned object types - return $this->matchTypeForUnionedObjectTypes($phpStanType); - } - - $nullabledTypeNode = $this->mapPHPStanTypeToPhpParserNode($nullabledType); - if ($nullabledTypeNode === null) { - return null; - } - - if ($nullabledTypeNode instanceof NullableType) { - return $nullabledTypeNode; - } - - if ($nullabledTypeNode instanceof PhpParserUnionType) { - throw new ShouldNotHappenException(); - } - - return new NullableType($nullabledTypeNode); - } - - if ($phpStanType instanceof NeverType || - $phpStanType instanceof VoidType || - $phpStanType instanceof MixedType || - $phpStanType instanceof ResourceType || - $phpStanType instanceof NullType - ) { - return null; - } - - if ($phpStanType instanceof ObjectWithoutClassType) { - if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::OBJECT_TYPE)) { - return new Identifier('object'); - } - - return null; - } - - throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + return $this->phpStanStaticTypeMapper->mapToPhpParserNode($phpStanType, $kind); } public function mapPHPStanTypeToDocString(Type $phpStanType, ?Type $parentType = null): string @@ -705,96 +500,6 @@ public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $ throw new NotImplementedException(__METHOD__ . ' for ' . get_class($typeNode)); } - private function matchArrayTypes(UnionType $unionType): ?Identifier - { - $isNullableType = false; - $hasIterable = false; - - foreach ($unionType->getTypes() as $unionedType) { - if ($unionedType instanceof IterableType) { - $hasIterable = true; - continue; - } - - if ($unionedType instanceof ArrayType) { - continue; - } - - if ($unionedType instanceof NullType) { - $isNullableType = true; - continue; - } - - if ($unionedType instanceof ObjectType) { - if ($unionedType->getClassName() === Traversable::class) { - $hasIterable = true; - continue; - } - } - - return null; - } - - $type = $hasIterable ? 'iterable' : 'array'; - if ($isNullableType) { - return new Identifier('?' . $type); - } - - return new Identifier($type); - } - - private function matchTypeForNullableUnionType(UnionType $unionType): ?Type - { - if (count($unionType->getTypes()) !== 2) { - return null; - } - - $firstType = $unionType->getTypes()[0]; - $secondType = $unionType->getTypes()[1]; - - if ($firstType instanceof NullType) { - return $secondType; - } - - if ($secondType instanceof NullType) { - return $firstType; - } - - return null; - } - - /** - * @return Name|FullyQualified|PhpParserUnionType|null - */ - private function matchTypeForUnionedObjectTypes(UnionType $unionType): ?Node - { - $phpParserUnionType = $this->matchPhpParserUnionType($unionType); - if ($phpParserUnionType !== null) { - return $phpParserUnionType; - } - - // do the type should be compatible with all other types, e.g. A extends B, B - foreach ($unionType->getTypes() as $unionedType) { - if (! $unionedType instanceof TypeWithClassName) { - return null; - } - - foreach ($unionType->getTypes() as $nestedUnionedType) { - if (! $nestedUnionedType instanceof TypeWithClassName) { - return null; - } - - if (! $this->areTypeWithClassNamesRelated($unionedType, $nestedUnionedType)) { - continue 2; - } - } - - return new FullyQualified($unionedType->getClassName()); - } - - return null; - } - private function mapScalarStringToType(string $scalarName): ?Type { $loweredScalarName = Strings::lower($scalarName); @@ -844,53 +549,4 @@ private function mapScalarStringToType(string $scalarName): ?Type return null; } - - private function matchPhpParserUnionType(UnionType $unionType): ?PhpParserUnionType - { - if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::UNION_TYPES)) { - return null; - } - - $phpParserUnionedTypes = []; - foreach ($unionType->getTypes() as $unionedType) { - /** @var Identifier|Name|null $phpParserNode */ - $phpParserNode = $this->mapPHPStanTypeToPhpParserNode($unionedType); - if ($phpParserNode === null) { - return null; - } - - $phpParserUnionedTypes[] = $phpParserNode; - } - - return new PhpParserUnionType($phpParserUnionedTypes); - } - - private function areTypeWithClassNamesRelated(TypeWithClassName $firstType, TypeWithClassName $secondType): bool - { - if (is_a($firstType->getClassName(), $secondType->getClassName(), true)) { - return true; - } - - return is_a($secondType->getClassName(), $firstType->getClassName(), true); - } - - private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes( - UnionTypeNode $unionTypeNode - ): AttributeAwareUnionTypeNode { - $unionedArrayType = []; - foreach ($unionTypeNode->types as $unionedType) { - if ($unionedType instanceof UnionTypeNode) { - foreach ($unionedType->types as $key => $subUnionedType) { - $unionedType->types[$key] = new ArrayTypeNode($subUnionedType); - } - - $unionedArrayType[] = $unionedType; - continue; - } - - $unionedArrayType[] = new ArrayTypeNode($unionedType); - } - - return new AttributeAwareUnionTypeNode($unionedArrayType); - } } diff --git a/packages/PHPStanStaticTypeMapper/config/config.yaml b/packages/PHPStanStaticTypeMapper/config/config.yaml new file mode 100644 index 000000000000..fd750ab3d7b8 --- /dev/null +++ b/packages/PHPStanStaticTypeMapper/config/config.yaml @@ -0,0 +1,8 @@ +services: + _defaults: + public: true + autowire: true + + Rector\PHPStanStaticTypeMapper\: + resource: '../src' + exclude: diff --git a/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php b/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php new file mode 100644 index 000000000000..0f87909fe7dd --- /dev/null +++ b/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php @@ -0,0 +1,420 @@ +phpVersionProvider = $phpVersionProvider; + } + + public function mapToPHPStanPhpDocTypeNode(Type $phpStanType) + { + if ($phpStanType instanceof MixedType) { + return new IdentifierTypeNode('mixed'); + } + + if ($phpStanType instanceof UnionType) { + $unionTypesNodes = []; + foreach ($phpStanType->getTypes() as $unionedType) { + $unionTypesNodes[] = $this->mapToPHPStanPhpDocTypeNode($unionedType); + } + + $unionTypesNodes = array_unique($unionTypesNodes); + + return new AttributeAwareUnionTypeNode($unionTypesNodes); + } + + if ($phpStanType instanceof ArrayType || $phpStanType instanceof IterableType) { + $itemTypeNode = $this->mapToPHPStanPhpDocTypeNode($phpStanType->getItemType()); + + if ($itemTypeNode instanceof UnionTypeNode) { + return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode); + } + + return new ArrayTypeNode($itemTypeNode); + } + + if ($phpStanType instanceof IntegerType) { + return new IdentifierTypeNode('int'); + } + + if ($phpStanType instanceof ClassStringType) { + return new IdentifierTypeNode('class-string'); + } + + if ($phpStanType instanceof StringType) { + return new IdentifierTypeNode('string'); + } + + if ($phpStanType instanceof BooleanType) { + return new IdentifierTypeNode('bool'); + } + + if ($phpStanType instanceof FloatType) { + return new IdentifierTypeNode('float'); + } + + if ($phpStanType instanceof ObjectType) { + return new IdentifierTypeNode('\\' . $phpStanType->getClassName()); + } + + if ($phpStanType instanceof NullType) { + return new IdentifierTypeNode('null'); + } + + if ($phpStanType instanceof NeverType) { + return new IdentifierTypeNode('mixed'); + } + + throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + } + + /** + * @return Identifier|Name|NullableType|PhpParserUnionType|null + */ + public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?Node + { + if ($phpStanType instanceof VoidType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::VOID_TYPE)) { + if (in_array($kind, ['param', 'property'], true)) { + // param cannot be void + return null; + } + + return new Identifier('void'); + } + + return null; + } + + if ($phpStanType instanceof SelfObjectType) { + return new Identifier('self'); + } + + if ($phpStanType instanceof IntegerType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { + return new Identifier('int'); + } + + return null; + } + + if ($phpStanType instanceof StringType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { + return new Identifier('string'); + } + + return null; + } + + if ($phpStanType instanceof BooleanType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { + return new Identifier('bool'); + } + + return null; + } + + if ($phpStanType instanceof FloatType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::SCALAR_TYPES)) { + return new Identifier('float'); + } + + return null; + } + + if ($phpStanType instanceof ArrayType) { + return new Identifier('array'); + } + + if ($phpStanType instanceof IterableType) { + return new Identifier('iterable'); + } + + if ($phpStanType instanceof ThisType) { + return new Identifier('self'); + } + + if ($phpStanType instanceof ParentStaticType) { + return new Identifier('parent'); + } + + if ($phpStanType instanceof StaticType) { + return null; + } + + if ($phpStanType instanceof CallableType || $phpStanType instanceof ClosureType) { + if ($kind === 'property') { + return null; + } + + return new Identifier('callable'); + } + + if ($phpStanType instanceof ShortenedObjectType) { + return new Name\FullyQualified($phpStanType->getFullyQualifiedName()); + } + + if ($phpStanType instanceof AliasedObjectType) { + return new Name($phpStanType->getClassName()); + } + + if ($phpStanType instanceof TypeWithClassName) { + $lowerCasedClassName = strtolower($phpStanType->getClassName()); + if ($lowerCasedClassName === 'callable') { + return new Identifier('callable'); + } + + if ($lowerCasedClassName === 'self') { + return new Identifier('self'); + } + + if ($lowerCasedClassName === 'static') { + return null; + } + + if ($lowerCasedClassName === 'mixed') { + return null; + } + + return new Name\FullyQualified($phpStanType->getClassName()); + } + + if ($phpStanType instanceof UnionType) { + // match array types + $arrayNode = $this->matchArrayTypes($phpStanType); + if ($arrayNode !== null) { + return $arrayNode; + } + + // special case for nullable + $nullabledType = $this->matchTypeForNullableUnionType($phpStanType); + if ($nullabledType === null) { + // use first unioned type in case of unioned object types + return $this->matchTypeForUnionedObjectTypes($phpStanType); + } + + $nullabledTypeNode = $this->mapToPhpParserNode($nullabledType); + if ($nullabledTypeNode === null) { + return null; + } + + if ($nullabledTypeNode instanceof NullableType) { + return $nullabledTypeNode; + } + + if ($nullabledTypeNode instanceof PhpParserUnionType) { + throw new ShouldNotHappenException(); + } + + return new NullableType($nullabledTypeNode); + } + + if ($phpStanType instanceof NeverType || + $phpStanType instanceof VoidType || + $phpStanType instanceof MixedType || + $phpStanType instanceof ResourceType || + $phpStanType instanceof NullType + ) { + return null; + } + + if ($phpStanType instanceof ObjectWithoutClassType) { + if ($this->phpVersionProvider->isAtLeast(PhpVersionFeature::OBJECT_TYPE)) { + return new Identifier('object'); + } + + return null; + } + + throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + } + + private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes( + UnionTypeNode $unionTypeNode + ): AttributeAwareUnionTypeNode { + $unionedArrayType = []; + foreach ($unionTypeNode->types as $unionedType) { + if ($unionedType instanceof UnionTypeNode) { + foreach ($unionedType->types as $key => $subUnionedType) { + $unionedType->types[$key] = new ArrayTypeNode($subUnionedType); + } + + $unionedArrayType[] = $unionedType; + continue; + } + + $unionedArrayType[] = new ArrayTypeNode($unionedType); + } + + return new AttributeAwareUnionTypeNode($unionedArrayType); + } + + private function matchTypeForNullableUnionType(UnionType $unionType): ?Type + { + if (count($unionType->getTypes()) !== 2) { + return null; + } + + $firstType = $unionType->getTypes()[0]; + $secondType = $unionType->getTypes()[1]; + + if ($firstType instanceof NullType) { + return $secondType; + } + + if ($secondType instanceof NullType) { + return $firstType; + } + + return null; + } + + /** + * @return Name|Name\FullyQualified|PhpParserUnionType|null + */ + private function matchTypeForUnionedObjectTypes(UnionType $unionType): ?Node + { + $phpParserUnionType = $this->matchPhpParserUnionType($unionType); + if ($phpParserUnionType !== null) { + return $phpParserUnionType; + } + + // do the type should be compatible with all other types, e.g. A extends B, B + foreach ($unionType->getTypes() as $unionedType) { + if (! $unionedType instanceof TypeWithClassName) { + return null; + } + + foreach ($unionType->getTypes() as $nestedUnionedType) { + if (! $nestedUnionedType instanceof TypeWithClassName) { + return null; + } + + if (! $this->areTypeWithClassNamesRelated($unionedType, $nestedUnionedType)) { + continue 2; + } + } + + return new Name\FullyQualified($unionedType->getClassName()); + } + + return null; + } + + private function areTypeWithClassNamesRelated(TypeWithClassName $firstType, TypeWithClassName $secondType): bool + { + if (is_a($firstType->getClassName(), $secondType->getClassName(), true)) { + return true; + } + + return is_a($secondType->getClassName(), $firstType->getClassName(), true); + } + + private function matchArrayTypes(UnionType $unionType): ?Identifier + { + $isNullableType = false; + $hasIterable = false; + + foreach ($unionType->getTypes() as $unionedType) { + if ($unionedType instanceof IterableType) { + $hasIterable = true; + continue; + } + + if ($unionedType instanceof ArrayType) { + continue; + } + + if ($unionedType instanceof NullType) { + $isNullableType = true; + continue; + } + + if ($unionedType instanceof ObjectType) { + if ($unionedType->getClassName() === Traversable::class) { + $hasIterable = true; + continue; + } + } + + return null; + } + + $type = $hasIterable ? 'iterable' : 'array'; + if ($isNullableType) { + return new Identifier('?' . $type); + } + + return new Identifier($type); + } + + private function matchPhpParserUnionType(UnionType $unionType): ?PhpParserUnionType + { + if (! $this->phpVersionProvider->isAtLeast(PhpVersionFeature::UNION_TYPES)) { + return null; + } + + $phpParserUnionedTypes = []; + foreach ($unionType->getTypes() as $unionedType) { + /** @var Identifier|Name|null $phpParserNode */ + $phpParserNode = $this->mapToPhpParserNode($unionedType); + if ($phpParserNode === null) { + return null; + } + + $phpParserUnionedTypes[] = $phpParserNode; + } + + return new PhpParserUnionType($phpParserUnionedTypes); + } +} From f03c521de0827a279daa62490d9be67240707edd Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 14 Jan 2020 15:25:42 +0100 Subject: [PATCH 3/3] make some use of TypeNodeResolver from PHPStan --- packages/NodeTypeResolver/config/config.yaml | 3 + .../PHPStanServicesFactory.php | 10 + .../NodeTypeResolver/src/StaticTypeMapper.php | 247 +++++------------- .../StaticTypeMapper/StaticTypeMapperTest.php | 5 +- .../src/PHPStanStaticTypeMapper.php | 136 ++++++++++ .../symfony_console_helper_set.php.inc | 76 ++++++ .../Component/Console/Helper/HelperSet.php | 14 + 7 files changed, 311 insertions(+), 180 deletions(-) create mode 100644 packages/TypeDeclaration/tests/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc create mode 100644 stubs/Symfony/Component/Console/Helper/HelperSet.php diff --git a/packages/NodeTypeResolver/config/config.yaml b/packages/NodeTypeResolver/config/config.yaml index b725bfb80473..bb2046e4757d 100644 --- a/packages/NodeTypeResolver/config/config.yaml +++ b/packages/NodeTypeResolver/config/config.yaml @@ -33,3 +33,6 @@ services: PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider: factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createOperatorTypeSpecifyingExtensionRegistryProvider'] + + PHPStan\PhpDoc\TypeNodeResolver: + factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createTypeNodeResolver'] diff --git a/packages/NodeTypeResolver/src/DependencyInjection/PHPStanServicesFactory.php b/packages/NodeTypeResolver/src/DependencyInjection/PHPStanServicesFactory.php index 263af0985b21..b535695f21c2 100644 --- a/packages/NodeTypeResolver/src/DependencyInjection/PHPStanServicesFactory.php +++ b/packages/NodeTypeResolver/src/DependencyInjection/PHPStanServicesFactory.php @@ -13,8 +13,13 @@ use PHPStan\DependencyInjection\ContainerFactory; use PHPStan\DependencyInjection\Type\DynamicReturnTypeExtensionRegistryProvider; use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider; +use PHPStan\PhpDoc\TypeNodeResolver; use PHPStan\Reflection\ReflectionProvider; +/** + * Factory so Symfony app can use services from PHPStan container + * @see packages/NodeTypeResolver/config/config.yaml:17 + */ final class PHPStanServicesFactory { /** @@ -101,4 +106,9 @@ public function createOperatorTypeSpecifyingExtensionRegistryProvider(): Operato { return $this->container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class); } + + public function createTypeNodeResolver(): TypeNodeResolver + { + return $this->container->getByType(TypeNodeResolver::class); + } } diff --git a/packages/NodeTypeResolver/src/StaticTypeMapper.php b/packages/NodeTypeResolver/src/StaticTypeMapper.php index 54ffee62ccc7..a87d0df69e55 100644 --- a/packages/NodeTypeResolver/src/StaticTypeMapper.php +++ b/packages/NodeTypeResolver/src/StaticTypeMapper.php @@ -4,7 +4,6 @@ namespace Rector\NodeTypeResolver; -use Closure; use Nette\Utils\Strings; use PhpParser\Node; use PhpParser\Node\Expr; @@ -12,8 +11,11 @@ use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; +use PhpParser\Node\Stmt\UseUse; use PhpParser\Node\UnionType as PhpParserUnionType; +use PHPStan\Analyser\NameScope; use PHPStan\Analyser\Scope; +use PHPStan\PhpDoc\TypeNodeResolver; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; @@ -29,15 +31,11 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\CallableType; use PHPStan\Type\ClassStringType; -use PHPStan\Type\ClosureType; use PHPStan\Type\ConstantType; use PHPStan\Type\FloatType; -use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\IntegerType; -use PHPStan\Type\IntersectionType; use PHPStan\Type\IterableType; use PHPStan\Type\MixedType; -use PHPStan\Type\NeverType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; @@ -46,16 +44,13 @@ use PHPStan\Type\StringType; use PHPStan\Type\ThisType; use PHPStan\Type\Type; -use PHPStan\Type\TypeWithClassName; -use PHPStan\Type\UnionType; use PHPStan\Type\VoidType; use Rector\BetterPhpDocParser\Type\PreSlashStringType; use Rector\Exception\NotImplementedException; use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; -use Rector\Php\PhpVersionProvider; -use Rector\PHPStan\Type\AliasedObjectType; +use Rector\PhpParser\Node\Resolver\NameResolver; use Rector\PHPStan\Type\FullyQualifiedObjectType; use Rector\PHPStan\Type\ParentStaticType; use Rector\PHPStan\Type\SelfObjectType; @@ -64,15 +59,10 @@ use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier; /** - * Maps PhpParser <=> PHPStan <=> PHPStan doc <=> string type nodes to between all possible formats + * Maps PhpParser <=> PHPStan <=> PHPStan doc <=> string type nodes between all possible formats */ final class StaticTypeMapper { - /** - * @var PhpVersionProvider - */ - private $phpVersionProvider; - /** * @var TypeFactory */ @@ -88,16 +78,28 @@ final class StaticTypeMapper */ private $phpStanStaticTypeMapper; + /** + * @var TypeNodeResolver + */ + private $typeNodeResolver; + + /** + * @var NameResolver + */ + private $nameResolver; + public function __construct( - PhpVersionProvider $phpVersionProvider, TypeFactory $typeFactory, ObjectTypeSpecifier $objectTypeSpecifier, - PHPStanStaticTypeMapper $phpStanStaticTypeMapper + PHPStanStaticTypeMapper $phpStanStaticTypeMapper, + TypeNodeResolver $typeNodeResolver, + NameResolver $nameResolver ) { - $this->phpVersionProvider = $phpVersionProvider; $this->typeFactory = $typeFactory; $this->objectTypeSpecifier = $objectTypeSpecifier; $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; + $this->typeNodeResolver = $typeNodeResolver; + $this->nameResolver = $nameResolver; } public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode @@ -115,137 +117,7 @@ public function mapPHPStanTypeToPhpParserNode(Type $phpStanType, ?string $kind = public function mapPHPStanTypeToDocString(Type $phpStanType, ?Type $parentType = null): string { - if ($phpStanType instanceof UnionType || $phpStanType instanceof IntersectionType) { - $stringTypes = []; - - foreach ($phpStanType->getTypes() as $unionedType) { - $stringTypes[] = $this->mapPHPStanTypeToDocString($unionedType); - } - - // remove empty values, e.g. void/iterable - $stringTypes = array_unique($stringTypes); - $stringTypes = array_filter($stringTypes); - - $joinCharacter = $phpStanType instanceof IntersectionType ? '&' : '|'; - - return implode($joinCharacter, $stringTypes); - } - - if ($phpStanType instanceof AliasedObjectType) { - // no preslash for alias - return $phpStanType->getClassName(); - } - - if ($phpStanType instanceof ShortenedObjectType) { - return '\\' . $phpStanType->getFullyQualifiedName(); - } - - if ($phpStanType instanceof FullyQualifiedObjectType) { - // always prefixed with \\ - return '\\' . $phpStanType->getClassName(); - } - - if ($phpStanType instanceof ObjectType) { - if (ClassExistenceStaticHelper::doesClassLikeExist($phpStanType->getClassName())) { - return '\\' . $phpStanType->getClassName(); - } - - return $phpStanType->getClassName(); - } - - if ($phpStanType instanceof ObjectWithoutClassType) { - return 'object'; - } - - if ($phpStanType instanceof ClosureType) { - return '\\' . Closure::class; - } - - if ($phpStanType instanceof StringType) { - return 'string'; - } - - if ($phpStanType instanceof IntegerType) { - return 'int'; - } - - if ($phpStanType instanceof NullType) { - return 'null'; - } - - if ($phpStanType instanceof ArrayType) { - if ($phpStanType->getItemType() instanceof UnionType) { - $unionedTypesAsString = []; - foreach ($phpStanType->getItemType()->getTypes() as $unionedArrayItemType) { - $unionedTypesAsString[] = $this->mapPHPStanTypeToDocString( - $unionedArrayItemType, - $phpStanType - ) . '[]'; - } - - $unionedTypesAsString = array_values($unionedTypesAsString); - $unionedTypesAsString = array_unique($unionedTypesAsString); - - return implode('|', $unionedTypesAsString); - } - - $docString = $this->mapPHPStanTypeToDocString($phpStanType->getItemType(), $parentType); - - // @todo improve this - $docStringTypes = explode('|', $docString); - $docStringTypes = array_filter($docStringTypes); - - foreach ($docStringTypes as $key => $docStringType) { - $docStringTypes[$key] = $docStringType . '[]'; - } - - return implode('|', $docStringTypes); - } - - if ($phpStanType instanceof MixedType) { - return 'mixed'; - } - - if ($phpStanType instanceof FloatType) { - return 'float'; - } - - if ($phpStanType instanceof VoidType) { - if ($this->phpVersionProvider->isAtLeast('7.1')) { - // the void type is better done in PHP code - return ''; - } - - // fallback for PHP 7.0 and older, where void type was only in docs - return 'void'; - } - - if ($phpStanType instanceof BooleanType) { - return 'bool'; - } - - if ($phpStanType instanceof IterableType) { - if ($this->phpVersionProvider->isAtLeast('7.1')) { - // the void type is better done in PHP code - return ''; - } - - return 'iterable'; - } - - if ($phpStanType instanceof NeverType) { - return 'mixed'; - } - - if ($phpStanType instanceof CallableType) { - return 'callable'; - } - - if ($phpStanType instanceof ResourceType) { - return 'resource'; - } - - throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + return $this->phpStanStaticTypeMapper->mapToDocString($phpStanType, $parentType); } public function mapPhpParserNodePHPStanType(Node $node): Type @@ -384,6 +256,27 @@ public function mapPHPStanPhpDocTypeNodeToPhpDocString(TypeNode $typeNode, Node } public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type + { + $nameScope = $this->createNameScopeFromNode($node); + // @todo use in the future + + $type = $this->typeNodeResolver->resolve($typeNode, $nameScope); + if ($typeNode instanceof GenericTypeNode) { + return $type; + } + + $customType = $this->customMapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, $node); + if ($type === $customType) { + return $type; + } + + return $customType; + } + + /** + * @deprecated Move gradualy to @see \PHPStan\PhpDoc\TypeNodeResolver from PHPStan + */ + private function customMapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type { if ($typeNode instanceof IdentifierTypeNode) { $type = $this->mapScalarStringToType($typeNode->name); @@ -467,36 +360,6 @@ public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $ return new ThisType($className); } - if ($typeNode instanceof GenericTypeNode) { - $genericMainType = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode->type, $node); - - if ($genericMainType instanceof TypeWithClassName) { - $mainTypeAsString = $genericMainType->getClassName(); - } else { - $mainTypeAsString = $typeNode->type->name; - } - - $genericTypes = []; - foreach ($typeNode->genericTypes as $genericTypeNode) { - $genericTypes[] = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($genericTypeNode, $node); - } - - // special use case for array - if (in_array($mainTypeAsString, ['array', 'iterable'], true)) { - $genericType = $this->typeFactory->createMixedPassedOrUnionType($genericTypes); - - if ($mainTypeAsString === 'array') { - return new ArrayType(new MixedType(), $genericType); - } - - if ($mainTypeAsString === 'iterable') { - return new IterableType(new MixedType(), $genericType); - } - } - - return new GenericObjectType($mainTypeAsString, $genericTypes); - } - throw new NotImplementedException(__METHOD__ . ' for ' . get_class($typeNode)); } @@ -549,4 +412,30 @@ private function mapScalarStringToType(string $scalarName): ?Type return null; } + + /** + * @see https://github.com/phpstan/phpstan-src/blob/8376548f76e2c845ae047e3010e873015b796818/src/Analyser/NameScope.php#L32 + */ + private function createNameScopeFromNode(Node $node): NameScope + { + $namespace = $node->getAttribute(AttributeKey::NAMESPACE_NAME); + $useNodes = $node->getAttribute(AttributeKey::USE_NODES); + + $uses = []; + if ($useNodes) { + foreach ($useNodes as $useNode) { + foreach ($useNode->uses as $useUse) { + /** @var UseUse $useUse */ + $aliasName = $useUse->getAlias()->name; + $useName = $this->nameResolver->getName($useUse->name); + + $uses[$aliasName] = $useName; + } + } + } + + $className = $node->getAttribute(AttributeKey::CLASS_NAME); + + return new NameScope($namespace, $uses, $className); + } } diff --git a/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php b/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php index 1c1dbe21c636..4550a273e279 100644 --- a/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php +++ b/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php @@ -49,7 +49,10 @@ public function provideDataForMapPHPStanPhpDocTypeNodeToPHPStanType(): Iterator $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('Traversable'), []); yield [$genericTypeNode, GenericObjectType::class]; - $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('iterable'), []); + $genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('iterable'), [ + new IdentifierTypeNode('string'), + ]); + yield [$genericTypeNode, IterableType::class]; } diff --git a/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php b/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php index 0f87909fe7dd..f89a8dcfeae7 100644 --- a/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php +++ b/packages/PHPStanStaticTypeMapper/src/PHPStanStaticTypeMapper.php @@ -4,6 +4,7 @@ namespace Rector\PHPStanStaticTypeMapper; +use Closure; use PhpParser\Node; use PhpParser\Node\Identifier; use PhpParser\Node\Name; @@ -19,6 +20,7 @@ use PHPStan\Type\ClosureType; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\IntersectionType; use PHPStan\Type\IterableType; use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; @@ -36,8 +38,10 @@ use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode; use Rector\Exception\NotImplementedException; use Rector\Exception\ShouldNotHappenException; +use Rector\NodeTypeResolver\ClassExistenceStaticHelper; use Rector\Php\PhpVersionProvider; use Rector\PHPStan\Type\AliasedObjectType; +use Rector\PHPStan\Type\FullyQualifiedObjectType; use Rector\PHPStan\Type\ParentStaticType; use Rector\PHPStan\Type\SelfObjectType; use Rector\PHPStan\Type\ShortenedObjectType; @@ -279,6 +283,138 @@ public function mapToPhpParserNode(Type $phpStanType, ?string $kind = null): ?No throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); } + public function mapToDocString(Type $phpStanType, ?Type $parentType = null): string + { + if ($phpStanType instanceof UnionType || $phpStanType instanceof IntersectionType) { + $stringTypes = []; + + foreach ($phpStanType->getTypes() as $unionedType) { + $stringTypes[] = $this->mapToDocString($unionedType); + } + + // remove empty values, e.g. void/iterable + $stringTypes = array_unique($stringTypes); + $stringTypes = array_filter($stringTypes); + + $joinCharacter = $phpStanType instanceof IntersectionType ? '&' : '|'; + + return implode($joinCharacter, $stringTypes); + } + + if ($phpStanType instanceof AliasedObjectType) { + // no preslash for alias + return $phpStanType->getClassName(); + } + + if ($phpStanType instanceof ShortenedObjectType) { + return '\\' . $phpStanType->getFullyQualifiedName(); + } + + if ($phpStanType instanceof FullyQualifiedObjectType) { + // always prefixed with \\ + return '\\' . $phpStanType->getClassName(); + } + + if ($phpStanType instanceof ObjectType) { + if (ClassExistenceStaticHelper::doesClassLikeExist($phpStanType->getClassName())) { + return '\\' . $phpStanType->getClassName(); + } + + return $phpStanType->getClassName(); + } + + if ($phpStanType instanceof ObjectWithoutClassType) { + return 'object'; + } + + if ($phpStanType instanceof ClosureType) { + return '\\' . Closure::class; + } + + if ($phpStanType instanceof StringType) { + return 'string'; + } + + if ($phpStanType instanceof IntegerType) { + return 'int'; + } + + if ($phpStanType instanceof NullType) { + return 'null'; + } + + if ($phpStanType instanceof ArrayType) { + if ($phpStanType->getItemType() instanceof UnionType) { + $unionedTypesAsString = []; + foreach ($phpStanType->getItemType()->getTypes() as $unionedArrayItemType) { + $unionedTypesAsString[] = $this->mapToDocString($unionedArrayItemType, $phpStanType) . '[]'; + } + + $unionedTypesAsString = array_values($unionedTypesAsString); + $unionedTypesAsString = array_unique($unionedTypesAsString); + + return implode('|', $unionedTypesAsString); + } + + $docString = $this->mapToDocString($phpStanType->getItemType(), $parentType); + + // @todo improve this + $docStringTypes = explode('|', $docString); + $docStringTypes = array_filter($docStringTypes); + + foreach ($docStringTypes as $key => $docStringType) { + $docStringTypes[$key] = $docStringType . '[]'; + } + + return implode('|', $docStringTypes); + } + + if ($phpStanType instanceof MixedType) { + return 'mixed'; + } + + if ($phpStanType instanceof FloatType) { + return 'float'; + } + + if ($phpStanType instanceof VoidType) { + if ($this->phpVersionProvider->isAtLeast('7.1')) { + // the void type is better done in PHP code + return ''; + } + + // fallback for PHP 7.0 and older, where void type was only in docs + return 'void'; + } + + if ($phpStanType instanceof BooleanType) { + return 'bool'; + } + + if ($phpStanType instanceof IterableType) { + if ($this->phpVersionProvider->isAtLeast('7.1')) { + // the void type is better done in PHP code + return ''; + } + + return 'iterable'; + } + + if ($phpStanType instanceof NeverType) { + return 'mixed'; + } + + if ($phpStanType instanceof CallableType) { + return 'callable'; + } + + if ($phpStanType instanceof ResourceType) { + return 'resource'; + } + + throw new NotImplementedException(__METHOD__ . ' for ' . get_class($phpStanType)); + } + private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes( UnionTypeNode $unionTypeNode ): AttributeAwareUnionTypeNode { diff --git a/packages/TypeDeclaration/tests/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc b/packages/TypeDeclaration/tests/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc new file mode 100644 index 000000000000..9f0701f01d75 --- /dev/null +++ b/packages/TypeDeclaration/tests/Rector/Property/CompleteVarDocTypePropertyRector/Fixture/symfony_console_helper_set.php.inc @@ -0,0 +1,76 @@ +application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * @return HelperSet + */ + public function getHelperSet() + { + return $this->helperSet; + } +} + +?> +----- +application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * @return HelperSet + */ + public function getHelperSet() + { + return $this->helperSet; + } +} + +?> diff --git a/stubs/Symfony/Component/Console/Helper/HelperSet.php b/stubs/Symfony/Component/Console/Helper/HelperSet.php new file mode 100644 index 000000000000..5d4d82c9a998 --- /dev/null +++ b/stubs/Symfony/Component/Console/Helper/HelperSet.php @@ -0,0 +1,14 @@ +