[TypeDeclaration] Improve param by caller type #2 (#5787)

This commit is contained in:
Tomas Votruba 2021-03-07 15:27:17 +01:00 committed by GitHub
parent e362a516c2
commit ba1444959d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
87 changed files with 1625 additions and 1343 deletions

View File

@ -191,6 +191,7 @@
],
"files": [
"vendor/nette/forms/src/Forms/Controls/SubmitButton.php",
"packages/node-type-resolver/tests/PerNodeTypeResolver/VariableTypeResolver/Fixture/this_class.php.inc",
"rules/restoration/tests/Rector/Use_/RestoreFullyQualifiedNameRector/Source/ShortClassOnly.php",
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/AnotherClass.php",
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/Foo.php",

View File

@ -14,6 +14,7 @@ use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayListItemNewlineFixer;
use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayOpenerAndCloserNewlineFixer;
use Symplify\CodingStandard\Fixer\ArrayNotation\StandaloneLineInMultilineArrayFixer;
use Symplify\CodingStandard\Fixer\Commenting\RemoveCommentedCodeFixer;
use Symplify\CodingStandard\Fixer\LineLength\DocBlockLineLengthFixer;
use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer;
use Symplify\EasyCodingStandard\ValueObject\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
@ -69,6 +70,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
// buggy - @todo fix on Symplify master
RemoveCommentedCodeFixer::class,
DocBlockLineLengthFixer::class,
// breaks on-purpose annotated variables
ReturnAssignmentFixer::class,

View File

@ -34,6 +34,7 @@ use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
@ -49,8 +50,8 @@ use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
use ReflectionMethod;
/**
* This service contains all the parsed nodes. E.g. all the functions, method call, classes, static calls etc.
* It's useful in case of context analysis, e.g. find all the usage of class method to detect, if the method is used.
* This service contains all the parsed nodes. E.g. all the functions, method call, classes, static calls etc. It's
* useful in case of context analysis, e.g. find all the usage of class method to detect, if the method is used.
*/
final class NodeRepository
{
@ -76,6 +77,7 @@ final class NodeRepository
/**
* E.g. [$this, 'someLocalMethod']
*
* @var array<string, array<string, ArrayCallable[]>>
*/
private $arrayCallablesByTypeAndMethod = [];
@ -150,6 +152,7 @@ final class NodeRepository
/**
* To prevent circular reference
*
* @required
*/
public function autowireNodeRepository(NodeTypeResolver $nodeTypeResolver): void
@ -831,7 +834,12 @@ final class NodeRepository
private function addCallByType(Node $node, Type $classType, string $methodName): void
{
if ($classType instanceof TypeWithClassName) {
if ($classType instanceof ThisType) {
$classType = $classType->getStaticObjectType();
}
$this->callsByTypeAndMethod[$classType->getClassName()][$methodName][] = $node;
$this->addParentTypeWithClassName($classType, $node, $methodName);
}
if ($classType instanceof UnionType) {
@ -862,4 +870,31 @@ final class NodeRepository
return $classReflections;
}
/**
* @param MethodCall|StaticCall $node
*/
private function addParentTypeWithClassName(
TypeWithClassName $typeWithClassName,
Node $node,
string $methodName
): void {
// include also parent types
if (! $typeWithClassName instanceof ObjectType) {
return;
}
if (! $this->reflectionProvider->hasClass($typeWithClassName->getClassName())) {
return;
}
$classReflection = $this->reflectionProvider->getClass($typeWithClassName->getClassName());
if (! $classReflection instanceof ClassReflection) {
return;
}
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
$this->callsByTypeAndMethod[$ancestorClassReflection->getName()][$methodName][] = $node;
}
}
}

View File

@ -1,76 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\NodeTypeCorrector;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
final class ParentClassLikeTypeCorrector
{
/**
* @var TypeFactory
*/
private $typeFactory;
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
/**
* @var ClassReflectionTypesResolver
*/
private $classReflectionTypesResolver;
public function __construct(
ClassReflectionTypesResolver $classReflectionTypesResolver,
ReflectionProvider $reflectionProvider,
TypeFactory $typeFactory
) {
$this->typeFactory = $typeFactory;
$this->reflectionProvider = $reflectionProvider;
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
}
public function correct(Type $type): Type
{
if ($type instanceof TypeWithClassName) {
if (! $this->reflectionProvider->hasClass($type->getClassName())) {
return $type;
}
$allTypes = $this->getClassLikeTypesByClassName($type->getClassName());
return $this->typeFactory->createObjectTypeOrUnionType($allTypes);
}
$classNames = TypeUtils::getDirectClassNames($type);
$allTypes = [];
foreach ($classNames as $className) {
if (! $this->reflectionProvider->hasClass($className)) {
continue;
}
$allTypes = array_merge($allTypes, $this->getClassLikeTypesByClassName($className));
}
return $this->typeFactory->createObjectTypeOrUnionType($allTypes);
}
/**
* @return string[]
*/
private function getClassLikeTypesByClassName(string $className): array
{
$classReflection = $this->reflectionProvider->getClass($className);
$classLikeTypes = $this->classReflectionTypesResolver->resolve($classReflection);
return array_unique($classLikeTypes);
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\NodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
@ -14,12 +15,17 @@ use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Property;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
@ -27,16 +33,17 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
use Rector\NodeTypeResolver\NodeTypeCorrector\ParentClassLikeTypeCorrector;
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
@ -61,11 +68,6 @@ final class NodeTypeResolver
*/
private $arrayTypeAnalyzer;
/**
* @var ParentClassLikeTypeCorrector
*/
private $parentClassLikeTypeCorrector;
/**
* @var TypeUnwrapper
*/
@ -86,16 +88,27 @@ final class NodeTypeResolver
*/
private $unionTypeFactory;
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @param NodeTypeResolverInterface[] $nodeTypeResolvers
*/
public function __construct(
ObjectTypeSpecifier $objectTypeSpecifier,
ParentClassLikeTypeCorrector $parentClassLikeTypeCorrector,
TypeUnwrapper $typeUnwrapper,
ClassAnalyzer $classAnalyzer,
GenericClassStringTypeCorrector $genericClassStringTypeCorrector,
UnionTypeFactory $unionTypeFactory,
ReflectionProvider $reflectionProvider,
NodeNameResolver $nodeNameResolver,
array $nodeTypeResolvers
) {
foreach ($nodeTypeResolvers as $nodeTypeResolver) {
@ -103,15 +116,17 @@ final class NodeTypeResolver
}
$this->objectTypeSpecifier = $objectTypeSpecifier;
$this->parentClassLikeTypeCorrector = $parentClassLikeTypeCorrector;
$this->typeUnwrapper = $typeUnwrapper;
$this->classAnalyzer = $classAnalyzer;
$this->genericClassStringTypeCorrector = $genericClassStringTypeCorrector;
$this->unionTypeFactory = $unionTypeFactory;
$this->reflectionProvider = $reflectionProvider;
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* Prevents circular dependency
*
* @required
*/
public function autowireNodeTypeResolver(ArrayTypeAnalyzer $arrayTypeAnalyzer): void
@ -135,37 +150,70 @@ final class NodeTypeResolver
public function isObjectType(Node $node, ObjectType $requiredObjectType): bool
{
if ($node instanceof ClassConstFetch) {
throw new ShouldNotHappenException();
}
$resolvedType = $this->resolve($node);
if ($resolvedType instanceof MixedType) {
return false;
}
// this should also work with ObjectType and UnionType with ObjectType
// use PHPStan types here
if ($resolvedType instanceof ThisType) {
$resolvedType = $resolvedType->getStaticObjectType();
}
if ($resolvedType instanceof ObjectType) {
return $this->isObjectTypeOfObjectType($resolvedType, $requiredObjectType);
}
if ($requiredObjectType->isSuperTypeOf($resolvedType)->yes()) {
return true;
}
if ($resolvedType->equals($requiredObjectType)) {
return true;
}
return $this->isMatchingUnionType($requiredObjectType, $resolvedType);
return $this->isMatchingUnionType($resolvedType, $requiredObjectType);
}
public function resolve(Node $node): Type
{
$type = $this->resolveFirstType($node);
if ($type instanceof IntersectionType) {
foreach ($type->getTypes() as $intersectionedType) {
if ($intersectionedType instanceof TypeWithClassName) {
return $this->parentClassLikeTypeCorrector->correct($intersectionedType);
}
}
}
if (! $type instanceof TypeWithClassName) {
$type = $this->resolveByNodeTypeResolvers($node);
if ($type !== null) {
return $type;
}
return $this->parentClassLikeTypeCorrector->correct($type);
$nodeScope = $node->getAttribute(AttributeKey::SCOPE);
if (! $nodeScope instanceof Scope) {
return new MixedType();
}
if (! $node instanceof Expr) {
return new MixedType();
}
// skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574
if ($node instanceof New_) {
$isAnonymousClass = $this->classAnalyzer->isAnonymousClass($node->class);
if ($isAnonymousClass) {
return new ObjectWithoutClassType();
}
}
$type = $nodeScope->getType($node);
// hot fix for phpstan not resolving chain method calls
if (! $node instanceof MethodCall) {
return $type;
}
if (! $type instanceof MixedType) {
return $type;
}
return $this->resolve($node->var);
}
/**
@ -374,6 +422,60 @@ final class NodeTypeResolver
return $this->isObjectType($classLike, $objectType);
}
public function resolveObjectTypeToCompare(Node $node): ?ObjectType
{
if ($node instanceof StaticCall) {
return $this->resolveStaticCallClassNameObjectTypeToCompare($node);
}
if ($node instanceof Stmt) {
$classLike = $node->getAttribute(ClassLike::class);
if ($classLike === null) {
return null;
}
$className = $this->nodeNameResolver->getName($classLike);
if ($className === null) {
return null;
}
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($className);
if ($classReflection instanceof ClassReflection) {
return new ObjectType($classReflection->getName(), null, $classReflection);
}
}
$callerType = $this->resolve($node);
if ($callerType instanceof ThisType) {
$callerType = $callerType->getStaticObjectType();
}
if (! $callerType instanceof ObjectType) {
return null;
}
return $callerType;
}
public function resolveObjectTypeFromScope(Scope $scope): ?ObjectType
{
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
$className = $classReflection->getName();
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
return new ObjectType($className, null, $classReflection);
}
private function addNodeTypeResolver(NodeTypeResolverInterface $nodeTypeResolver): void
{
foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) {
@ -381,68 +483,26 @@ final class NodeTypeResolver
}
}
private function isMatchingUnionType(ObjectType $requiredObjectType, Type $resolvedType): bool
/**
* @deprecated
*/
private function isMatchingUnionType(Type $resolvedType, ObjectType $requiredObjectType): bool
{
if (! $resolvedType instanceof UnionType) {
return false;
}
foreach ($resolvedType->getTypes() as $unionedType) {
if ($unionedType instanceof TypeWithClassName && is_a(
$unionedType->getClassName(),
$requiredObjectType->getClassName(),
true
)) {
return true;
}
$type = TypeCombinator::removeNull($resolvedType);
if (! $unionedType->equals($requiredObjectType)) {
continue;
}
// for falsy nullables
$type = TypeCombinator::remove($type, new ConstantBooleanType(false));
if ($unionedType->equals($requiredObjectType)) {
return true;
}
if (! $type instanceof ObjectType) {
return false;
}
return false;
}
private function resolveFirstType(Node $node): Type
{
$type = $this->resolveByNodeTypeResolvers($node);
if ($type !== null) {
return $type;
}
$nodeScope = $node->getAttribute(AttributeKey::SCOPE);
if (! $nodeScope instanceof Scope) {
return new MixedType();
}
if (! $node instanceof Expr) {
return new MixedType();
}
// skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574
if ($node instanceof New_) {
$isAnonymousClass = $this->classAnalyzer->isAnonymousClass($node->class);
if ($isAnonymousClass) {
return new ObjectWithoutClassType();
}
}
$type = $nodeScope->getType($node);
// hot fix for phpstan not resolving chain method calls
if (! $node instanceof MethodCall) {
return $type;
}
if (! $type instanceof MixedType) {
return $type;
}
return $this->resolveFirstType($node->var);
return $type->isInstanceOf($requiredObjectType->getClassName())
->yes();
}
private function resolveArrayType(Expr $expr): Type
@ -534,4 +594,59 @@ final class NodeTypeResolver
return $otherType;
}
private function resolveStaticCallClassNameObjectTypeToCompare(StaticCall $staticCall): ?ObjectType
{
$className = $this->nodeNameResolver->getName($staticCall->class);
if ($className === 'parent') {
/** @var Scope $scope */
$scope = $staticCall->getAttribute(AttributeKey::SCOPE);
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
throw new ShouldNotHappenException();
}
$className = $classReflection->getName();
}
if ($className === null) {
return null;
}
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($className);
return new ObjectType($classReflection->getName(), null, $classReflection);
}
private function isObjectTypeOfObjectType(ObjectType $resolvedObjectType, ObjectType $requiredObjectType): bool
{
if ($resolvedObjectType->isInstanceOf($requiredObjectType->getClassName())->yes()) {
return true;
}
if ($resolvedObjectType->getClassName() === $requiredObjectType->getClassName()) {
return true;
}
if (! $this->reflectionProvider->hasClass($resolvedObjectType->getClassName())) {
return false;
}
$classReflection = $this->reflectionProvider->getClass($resolvedObjectType->getClassName());
if ($classReflection instanceof ClassReflection) {
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
if ($ancestorClassReflection->hasTraitUse($requiredObjectType->getClassName())) {
return true;
}
}
}
return $classReflection->isSubclassOf($requiredObjectType->getClassName());
}
}

View File

@ -10,30 +10,17 @@ use PhpParser\Node\Stmt\Interface_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
/**
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\ClassTypeResolverTest
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\InterfaceTypeResolverTest
*/
final class ClassAndInterfaceTypeResolver implements NodeTypeResolverInterface
{
/**
* @var ClassReflectionTypesResolver
*/
private $classReflectionTypesResolver;
/**
* @var TypeFactory
*/
private $typeFactory;
public function __construct(ClassReflectionTypesResolver $classReflectionTypesResolver, TypeFactory $typeFactory)
{
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
$this->typeFactory = $typeFactory;
}
/**
* @return array<class-string<Node>>
*/
@ -58,8 +45,6 @@ final class ClassAndInterfaceTypeResolver implements NodeTypeResolverInterface
return new MixedType();
}
$classTypes = $this->classReflectionTypesResolver->resolve($classReflection);
return $this->typeFactory->createObjectTypeOrUnionType($classTypes);
return new ObjectType($classReflection->getName(), null, $classReflection);
}
}

View File

@ -1,62 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\NodeTypeResolver;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class ClassConstFetchTypeResolver implements NodeTypeResolverInterface
{
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* To avoid cricular references
* @required
*/
public function autowireClassConstFetchTypeResolver(
NodeTypeResolver $nodeTypeResolver,
NodeNameResolver $nodeNameResolver
): void {
$this->nodeTypeResolver = $nodeTypeResolver;
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return array<class-string<Node>>
*/
public function getNodeClasses(): array
{
return [ClassConstFetch::class];
}
/**
* @param ClassConstFetch $node
*/
public function resolve(Node $node): Type
{
if ($this->nodeNameResolver->isName($node->name, 'class')) {
$className = $this->nodeNameResolver->getName($node->class);
if ($className !== null) {
return new GenericClassStringType(new ObjectType($className));
}
}
return $this->nodeTypeResolver->resolve($node->class);
}
}

View File

@ -101,18 +101,17 @@ final class VariableTypeResolver implements NodeTypeResolverInterface
private function resolveTypesFromScope(Variable $variable, string $variableName): Type
{
$nodeScope = $this->resolveNodeScope($variable);
if (! $nodeScope instanceof Scope) {
$scope = $this->resolveNodeScope($variable);
if (! $scope instanceof Scope) {
return new MixedType();
}
if (! $nodeScope->hasVariableType($variableName)->yes()) {
if (! $scope->hasVariableType($variableName)->yes()) {
return new MixedType();
}
// this → object type is easier to work with and consistent with the rest of the code
return $nodeScope->getVariableType($variableName);
return $scope->getVariableType($variableName);
}
private function resolveNodeScope(Variable $variable): ?Scope

View File

@ -178,15 +178,17 @@ final class PHPStanNodeScopeResolver
/**
* @param Class_|Interface_ $classLike
*/
private function resolveClassOrInterfaceScope(ClassLike $classLike, Scope $scope): MutatingScope
private function resolveClassOrInterfaceScope(ClassLike $classLike, Scope $scope): Scope
{
$className = $this->resolveClassName($classLike);
// is anonymous class? - not possible to enter it since PHPStan 0.12.33, see https://github.com/phpstan/phpstan-src/commit/e87fb0ec26f9c8552bbeef26a868b1e5d8185e91
if ($classLike instanceof Class_ && Strings::match($className, self::ANONYMOUS_CLASS_START_REGEX)) {
$classReflection = $this->reflectionProvider->getAnonymousClassReflection($classLike, $scope);
} else {
} elseif ($this->reflectionProvider->hasClass($className)) {
$classReflection = $this->reflectionProvider->getClass($className);
} else {
return $scope;
}
/** @var MutatingScope $scope */

View File

@ -12,12 +12,10 @@ use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
@ -56,24 +54,6 @@ final class TypeFactory
return $this->createUnionOrSingleType($types);
}
/**
* @param string[] $allTypes
* @return ObjectType|UnionType
*/
public function createObjectTypeOrUnionType(array $allTypes): Type
{
if (count($allTypes) === 1) {
return new ObjectType($allTypes[0]);
}
if (count($allTypes) > 1) {
// keep original order, UnionType internally overrides it → impossible to get first type back, e.g. class over interface
return $this->unionTypeFactory->createUnionObjectType($allTypes);
}
throw new ShouldNotHappenException();
}
/**
* @param Type[] $types
* @return Type[]

View File

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Reflection;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
final class ClassReflectionTypesResolver
{
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
/**
* Warning: Includes original class as well to normalize all types to strings!
*
* @return string[]
*/
public function resolve(ClassReflection $classReflection): array
{
// current class
$types = [$classReflection->getName()];
// parent classes
$types = array_merge($types, $classReflection->getParentClassesNames());
// interfaces
foreach ($classReflection->getInterfaces() as $interfaceReflection) {
$types[] = $interfaceReflection->getName();
}
foreach ($classReflection->getInterfaces() as $interfaceReflection) {
$types[] = $interfaceReflection->getName();
}
// traits
foreach ($classReflection->getTraits() as $traitReflection) {
$types[] = $traitReflection->getName();
}
// to cover traits of parent classes
foreach ($classReflection->getParentClassesNames() as $parentClassName) {
$parentClassReflection = $this->reflectionProvider->getClass($parentClassName);
foreach ($parentClassReflection->getTraits() as $parentClassTrait) {
$types[] = $parentClassTrait->getName();
}
}
return array_unique($types);
}
}

View File

@ -6,16 +6,13 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTyp
use Iterator;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\Type;
use PHPStan\Type\ObjectType;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\AnotherTrait;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentClass;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentInterface;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithParentTrait;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ClassWithTrait;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\ParentClass;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\SomeInterface;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
/**
* @see \Rector\NodeTypeResolver\NodeTypeResolver\ClassAndInterfaceTypeResolver
@ -25,55 +22,35 @@ final class ClassTypeResolverTest extends AbstractNodeTypeResolverTest
/**
* @dataProvider dataProvider()
*/
public function test(string $file, int $nodePosition, Type $expectedType): void
public function test(string $file, int $nodePosition, ObjectType $expectedObjectType): void
{
$variableNodes = $this->getNodesForFileOfType($file, Class_::class);
$resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]);
$this->assertEquals($expectedType, $resolvedType);
$this->assertInstanceOf(TypeWithClassName::class, $resolvedType);
/** @var TypeWithClassName $resolvedType */
$this->assertEquals($expectedObjectType->getClassName(), $resolvedType->getClassName());
}
public function dataProvider(): Iterator
{
$unionTypeFactory = new UnionTypeFactory();
yield [
__DIR__ . '/Source/ClassWithParentInterface.php',
0,
$unionTypeFactory->createUnionObjectType([ClassWithParentInterface::class, SomeInterface::class]),
new ObjectType(ClassWithParentInterface::class),
];
yield [
__DIR__ . '/Source/ClassWithParentClass.php',
0,
$unionTypeFactory->createUnionObjectType([ClassWithParentClass::class, ParentClass::class]),
];
yield [__DIR__ . '/Source/ClassWithParentClass.php', 0, new ObjectType(ClassWithParentClass::class)];
yield [
__DIR__ . '/Source/ClassWithTrait.php',
0,
$unionTypeFactory->createUnionObjectType([ClassWithTrait::class, AnotherTrait::class]),
];
yield [__DIR__ . '/Source/ClassWithTrait.php', 0, new ObjectType(ClassWithTrait::class)];
yield [
__DIR__ . '/Source/ClassWithParentTrait.php',
0,
$unionTypeFactory->createUnionObjectType(
[ClassWithParentTrait::class, ClassWithTrait::class, AnotherTrait::class]
),
];
yield [__DIR__ . '/Source/ClassWithParentTrait.php', 0, new ObjectType(ClassWithParentTrait::class)];
yield [
__DIR__ . '/Source/AnonymousClass.php',
0,
$unionTypeFactory->createUnionObjectType(
[
'AnonymousClassdefa360846b84894d4be1b25c2ce6da9',
ParentClass::class,
SomeInterface::class,
AnotherTrait::class,
]
),
new ObjectType('AnonymousClassdefa360846b84894d4be1b25c2ce6da9'),
];
}
}

View File

@ -6,11 +6,10 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTyp
use Iterator;
use PhpParser\Node\Stmt\Interface_;
use PHPStan\Type\Type;
use PHPStan\Type\ObjectType;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\SomeInterfaceWithParentInterface;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ClassAndInterfaceTypeResolver\Source\SomeParentInterface;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
/**
* @see \Rector\NodeTypeResolver\NodeTypeResolver\ClassAndInterfaceTypeResolver
@ -20,22 +19,23 @@ final class InterfaceTypeResolverTest extends AbstractNodeTypeResolverTest
/**
* @dataProvider dataProvider()
*/
public function test(string $file, int $nodePosition, Type $expectedType): void
public function test(string $file, int $nodePosition, TypeWithClassName $expectedTypeWithClassName): void
{
$variableNodes = $this->getNodesForFileOfType($file, Interface_::class);
$resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]);
$this->assertEquals($expectedType, $resolvedType);
$this->assertInstanceOf(TypeWithClassName::class, $resolvedType);
/** @var TypeWithClassName $resolvedType */
$this->assertEquals($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName());
}
public function dataProvider(): Iterator
{
$unionTypeFactory = new UnionTypeFactory();
$unionType = $unionTypeFactory->createUnionObjectType(
[SomeInterfaceWithParentInterface::class, SomeParentInterface::class]
);
yield [__DIR__ . '/Source/SomeInterfaceWithParentInterface.php', 0, $unionType];
yield [
__DIR__ . '/Source/SomeInterfaceWithParentInterface.php',
0,
new ObjectType(SomeInterfaceWithParentInterface::class),
];
}
}

View File

@ -7,7 +7,7 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ParamTypeResolver;
use Iterator;
use PhpParser\Node\Param;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\ParamTypeResolver\Source\Html;
@ -19,12 +19,16 @@ final class ParamTypeResolverTest extends AbstractNodeTypeResolverTest
/**
* @dataProvider provideData()
*/
public function test(string $file, int $nodePosition, Type $expectedType): void
public function test(string $file, int $nodePosition, TypeWithClassName $expectedTypeWithClassName): void
{
$variableNodes = $this->getNodesForFileOfType($file, Param::class);
$resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]);
$this->assertEquals($expectedType, $resolvedType);
$this->assertInstanceOf(TypeWithClassName::class, $resolvedType);
/** @var TypeWithClassName $resolvedType */
$this->assertSame($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName());
}
public function provideData(): Iterator

View File

@ -46,11 +46,7 @@ final class PropertyTypeResolverTest extends AbstractNodeTypeResolverTest
yield [__DIR__ . '/Source/MethodParamDocBlock.php', 0, new ObjectType(Html::class)];
yield [
__DIR__ . '/Source/MethodParamDocBlock.php',
1,
$unionTypeFactory->createUnionObjectType([ClassThatExtendsHtml::class, Html::class]),
];
yield [__DIR__ . '/Source/MethodParamDocBlock.php', 1, new ObjectType(ClassThatExtendsHtml::class)];
// mimics failing test from DomainDrivenDesign set
$unionType = $unionTypeFactory->createUnionObjectType([SomeChild::class, new NullType()]);

View File

@ -1,6 +1,8 @@
<?php
namespace SomeNamespace;
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Fixture;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType;

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Fixture;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\FirstType;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\SecondType;
final class AssignmentClass
{
/**
* @var FirstType
*/
private $property;
public function getValue(): SecondType
{
$variable = new AnotherType();
$assignedVariable = $variable;
}
}
$someClass = new NewClass;

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Fixture;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\FirstType;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\SecondType;
final class NewClass
{
/**
* @var FirstType
*/
private $property;
public function getValue(): SecondType
{
$variable = new AnotherType();
$variable->test();
$assignedVariable = $variable;
}
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source;
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Fixture;
use Rector\NodeTypeResolver\Tests\Source\AnotherClass;

View File

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source;
final class AssignmentClass
{
/**
* @var FirstType
*/
private $property;
public function getValue(): SecondType
{
$variable = new AnotherType;
$assignedVariable = $variable;
}
}
$someClass = new NewClass;

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source;
final class NewClass
{
/**
* @var FirstType
*/
private $property;
public function getValue(): SecondType
{
$variable = new AnotherType;
$variable->test();
$assignedVariable = $variable;
}
}

View File

@ -7,12 +7,12 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver
use Iterator;
use PhpParser\Node\Expr\Variable;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\ThisType;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Fixture\ThisClass;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\AnotherType;
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\VariableTypeResolver\Source\ThisClass;
use Rector\NodeTypeResolver\Tests\Source\AnotherClass;
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
use ReflectionClass;
/**
* @see \Rector\NodeTypeResolver\NodeTypeResolver\VariableTypeResolver
@ -22,29 +22,25 @@ final class VariableTypeResolverTest extends AbstractNodeTypeResolverTest
/**
* @dataProvider provideData()
*/
public function test(string $file, int $nodePosition, Type $expectedType): void
public function test(string $file, int $nodePosition, TypeWithClassName $expectedTypeWithClassName): void
{
$variableNodes = $this->getNodesForFileOfType($file, Variable::class);
$resolvedType = $this->nodeTypeResolver->resolve($variableNodes[$nodePosition]);
$this->assertEquals($expectedType, $resolvedType);
$this->assertInstanceOf(TypeWithClassName::class, $resolvedType);
/** @var TypeWithClassName $resolvedType */
$this->assertEquals($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName());
}
public function provideData(): Iterator
{
$unionTypeFactory = new UnionTypeFactory();
yield [
__DIR__ . '/Source/ThisClass.php',
0,
$unionTypeFactory->createUnionObjectType([ThisClass::class, AnotherClass::class]),
];
yield [__DIR__ . '/Fixture/this_class.php.inc', 0, new ThisType(new ReflectionClass(ThisClass::class))];
$anotherTypeObjectType = new ObjectType(AnotherType::class);
yield [__DIR__ . '/Source/NewClass.php', 1, $anotherTypeObjectType];
yield [__DIR__ . '/Source/NewClass.php', 3, $anotherTypeObjectType];
yield [__DIR__ . '/Source/AssignmentClass.php', 2, $anotherTypeObjectType];
yield [__DIR__ . '/Source/ArgumentTypehint.php', 1, $anotherTypeObjectType];
yield [__DIR__ . '/Fixture/new_class.php.inc', 1, $anotherTypeObjectType];
yield [__DIR__ . '/Fixture/new_class.php.inc', 3, $anotherTypeObjectType];
yield [__DIR__ . '/Fixture/assignment_class.php.inc', 2, $anotherTypeObjectType];
yield [__DIR__ . '/Fixture/argument_typehint.php.inc', 1, $anotherTypeObjectType];
}
}

View File

@ -9,6 +9,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\ClassLike;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
@ -17,22 +18,25 @@ use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
final class UnionTypeCommonTypeNarrower
{
/**
* Key = the winner
* Array = the group of types matched
* Key = the winner Array = the group of types matched
*
* @var array<class-string<Node|\PHPStan\PhpDocParser\Ast\Node>, array<class-string<Node|\PHPStan\PhpDocParser\Ast\Node>>>
* @var array<string, array<class-string<Node>|class-string<\PHPStan\PhpDocParser\Ast\Node>|class-string<RectorInterface>>>
*/
private const PRIORITY_TYPES = [
ClassLike::class => [ClassLike::class],
FunctionLike::class => [FunctionLike::class],
BinaryOp::class => [BinaryOp::class, Expr::class],
Expr::class => [Node::class, Expr::class],
Stmt::class => [Node::class, Stmt::class],
PhpDocTagValueNode::class => [PhpDocTagValueNode::class, \PHPStan\PhpDocParser\Ast\Node::class],
Node::class => [Node::class],
RectorInterface::class => [RectorInterface::class],
];
/**
@ -120,10 +124,15 @@ final class UnionTypeCommonTypeNarrower
$typeClassReflections = $this->resolveClassParentClassesAndInterfaces($unionedType);
$typeClassNames = [];
foreach ($typeClassReflections as $typeClassReflection) {
$typeClassNames[] = $typeClassReflection->getName();
}
if ($typeClassNames === []) {
continue;
}
$availableTypes[] = $typeClassNames;
}
@ -159,9 +168,12 @@ final class UnionTypeCommonTypeNarrower
*/
private function narrowAvailableTypes(array $availableTypes): array
{
if (count($availableTypes) < 2) {
return [];
}
/** @var string[] $sharedTypes */
$sharedTypes = array_intersect(...$availableTypes);
return array_values($sharedTypes);
}

View File

@ -10,6 +10,7 @@ use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\NullableType;
use PhpParser\Node\UnionType as PhpParserUnionType;
use PhpParser\NodeAbstract;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\IterableType;
use PHPStan\Type\NullType;
@ -22,6 +23,7 @@ use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode;
use Rector\CodeQuality\Tests\Rector\If_\ExplicitBoolCompareRector\Fixture\Nullable;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer;
@ -295,7 +297,7 @@ final class UnionTypeMapper implements TypeMapperInterface
$sharedTypeWithClassName = $this->matchTwoObjectTypes($unionType);
if ($sharedTypeWithClassName instanceof TypeWithClassName) {
return $sharedTypeWithClassName;
return $this->correctObjectType($sharedTypeWithClassName);
}
// find least common denominator
@ -332,4 +334,17 @@ final class UnionTypeMapper implements TypeMapperInterface
return null;
}
private function correctObjectType(TypeWithClassName $typeWithClassName): TypeWithClassName
{
if ($typeWithClassName->getClassName() === NodeAbstract::class) {
return new ObjectType('PhpParser\Node');
}
if ($typeWithClassName->getClassName() === AbstractRector::class) {
return new ObjectType('Rector\Core\Contract\Rector\RectorInterface');
}
return $typeWithClassName;
}
}

View File

@ -616,3 +616,13 @@ parameters:
- src/Console/Command/ProcessCommand.php
- '#Content of method "getNextExpression\(\)" is duplicated with method "getNextExpression\(\)" in "Rector\\CodingStyle\\Rector\\Assign\\ManualJsonStringToJsonEncodeArrayRector" class\. Use unique content or service instead#'
- '#Content of method "getFunctionLikePhpDocInfo\(\)" is duplicated with method "getFunctionLikePhpDocInfo\(\)" in "Rector\\NodeTypeResolver\\NodeTypeResolver\\ParamTypeResolver" class\. Use unique content or service instead#'
# faking phpstan reflection in tests
- '#Parameter \#1 \$classReflection of class PHPStan\\Type\\ThisType constructor expects PHPStan\\Reflection\\ClassReflection\|string, ReflectionClass<Rector\\NodeTypeResolver\\Tests\\PerNodeTypeResolver\\VariableTypeResolver\\Fixture\\ThisClass\> given#'
-
message: '#Instead of "ReflectionClass" class/interface use "PHPStan\\Reflection\\ClassReflection"#'
paths:
- packages/node-type-resolver/tests/PerNodeTypeResolver/VariableTypeResolver/VariableTypeResolverTest.php
- '#Cognitive complexity for "Rector\\EarlyReturn\\Rector\\If_\\ChangeAndIfToEarlyReturnRector\:\:refactor\(\)" is 10, keep it under 9#'

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector;
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
use Rector\CodingStyle\ValueObject\PreferenceSelfThis;
use Rector\Core\Configuration\Option;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersion;
@ -31,7 +32,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(PreferThisOrSelfMethodCallRector::class)
->call('configure', [[
PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [
TestCase::class => PreferThisOrSelfMethodCallRector::PREFER_THIS,
TestCase::class => PreferenceSelfThis::PREFER_THIS,
],
]]);

View File

@ -11,6 +11,7 @@ use PhpParser\Node\Scalar\String_;
use PHPStan\Reflection\Php\PhpMethodReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\Core\Exception\ShouldNotHappenException;
@ -64,6 +65,11 @@ final class CallableClassMethodMatcher
}
$objectType = $this->nodeTypeResolver->resolve($objectExpr);
if ($objectType instanceof ThisType) {
$objectType = $objectType->getStaticObjectType();
}
$objectType = $this->popFirstObjectType($objectType);
if ($objectType instanceof ObjectType) {

View File

@ -97,12 +97,14 @@ CODE_SAMPLE
}
$propertyFetchVarType = $this->getObjectType($issetVar->var);
if ($propertyFetchVarType instanceof TypeWithClassName) {
if (! $this->reflectionProvider->hasClass($propertyFetchVarType->getClassName())) {
continue;
}
$classReflection = $this->reflectionProvider->getClass($propertyFetchVarType->getClassName());
if (! $classReflection->hasProperty($propertyFetchName)) {
$newNodes[] = $this->replaceToPropertyExistsWithNullCheck(
$issetVar->var,

View File

@ -12,7 +12,6 @@ final class PropertyAfterInstantiationIfAlwaysExists
$obj = new self();
isset($obj->x);
isset($obj->y);
isset($obj->x) && isset($obj->y);
}
}
@ -32,7 +31,6 @@ final class PropertyAfterInstantiationIfAlwaysExists
$obj = new self();
$obj->x !== null;
$obj->y !== null;
$obj->x !== null && $obj->y !== null;
}
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Type\ObjectType;
use Rector\CodingStyle\ValueObject\PreferenceSelfThis;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Exception\Configuration\InvalidConfigurationException;
use Rector\Core\Rector\AbstractRector;
@ -26,23 +27,6 @@ final class PreferThisOrSelfMethodCallRector extends AbstractRector implements C
*/
public const TYPE_TO_PREFERENCE = 'type_to_preference';
/**
* @api
* @var string
*/
public const PREFER_SELF = self::SELF;
/**
* @api
* @var string
*/
public const PREFER_THIS = 'this';
/**
* @var string[]
*/
private const ALLOWED_OPTIONS = [self::PREFER_THIS, self::PREFER_SELF];
/**
* @var string
*/
@ -79,7 +63,7 @@ CODE_SAMPLE
,
[
self::TYPE_TO_PREFERENCE => [
'PHPUnit\Framework\TestCase' => self::PREFER_SELF,
'PHPUnit\Framework\TestCase' => PreferenceSelfThis::PREFER_SELF,
],
]
),
@ -104,7 +88,7 @@ CODE_SAMPLE
continue;
}
if ($preference === self::PREFER_SELF) {
if ($preference === PreferenceSelfThis::PREFER_SELF) {
return $this->processToSelf($node);
}
@ -115,7 +99,7 @@ CODE_SAMPLE
}
/**
* @param mixed[] $configuration
* @param array<string, array<class-string, string>> $configuration
*/
public function configure(array $configuration): void
{
@ -173,7 +157,7 @@ CODE_SAMPLE
private function ensurePreferenceIsValid(string $preference): void
{
if (in_array($preference, self::ALLOWED_OPTIONS, true)) {
if (in_array($preference, PreferenceSelfThis::ALLOWED_VALUES, true)) {
return;
}
@ -181,7 +165,7 @@ CODE_SAMPLE
'Preference configuration "%s" for "%s" is not valid. Use one of "%s"',
$preference,
self::class,
implode('", "', self::ALLOWED_OPTIONS)
implode('", "', PreferenceSelfThis::ALLOWED_VALUES)
));
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\ValueObject;
/**
* @enum
*/
final class PreferenceSelfThis
{
/**
* @var string[]
*/
public const ALLOWED_VALUES = [self::PREFER_THIS, self::PREFER_SELF];
/**
* @api
* @var string
*/
public const PREFER_THIS = 'prefer_this';
/**
* @api
* @var string
*/
public const PREFER_SELF = 'prefer_self';
}

View File

@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase;
use Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector;
use Rector\CodingStyle\Tests\Rector\MethodCall\PreferThisOrSelfMethodCallRector\Source\AbstractTestCase;
use Rector\CodingStyle\Tests\Rector\MethodCall\PreferThisOrSelfMethodCallRector\Source\BeLocalClass;
use Rector\CodingStyle\ValueObject\PreferenceSelfThis;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -11,9 +12,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(PreferThisOrSelfMethodCallRector::class)
->call('configure', [[
PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [
AbstractTestCase::class => 'self',
BeLocalClass::class => 'this',
TestCase::class => 'self',
AbstractTestCase::class => PreferenceSelfThis::PREFER_SELF,
BeLocalClass::class => PreferenceSelfThis::PREFER_THIS,
TestCase::class => PreferenceSelfThis::PREFER_SELF,
],
]]);
};

View File

@ -6,7 +6,7 @@ namespace Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Fi
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping as ORM;
use Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Source\User;
final class SomeServiceEntityRepository extends ServiceEntityRepository
{
@ -23,10 +23,6 @@ final class SomeServiceEntityRepository extends ServiceEntityRepository
}
}
class User
{
}
?>
-----
<?php
@ -37,12 +33,12 @@ namespace Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Fi
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\Mapping as ORM;
use Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Source\User;
final class SomeServiceEntityRepository
{
/**
* @var \Doctrine\ORM\EntityRepository<\Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Fixture\User>
* @var \Doctrine\ORM\EntityRepository<\Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Source\User>
*/
private \Doctrine\ORM\EntityRepository $repository;
public function __construct(private \Doctrine\ORM\EntityManagerInterface $entityManager)
@ -58,8 +54,4 @@ final class SomeServiceEntityRepository
}
}
class User
{
}
?>

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\DoctrineCodeQuality\Tests\Set\DoctrineRepositoryAsServiceSet\Source;
final class User
{
}

View File

@ -8,6 +8,5 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [SetList::DOCTRINE_REPOSITORY_AS_SERVICE]);
};

View File

@ -9,6 +9,7 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
@ -158,8 +159,7 @@ CODE_SAMPLE
}
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
if (! $classReflection instanceof ClassReflection) {
// possibly trait/interface
return true;
}

View File

@ -6,10 +6,8 @@ namespace Rector\Doctrine\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -81,11 +79,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
$callerObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node->var);
if (! $callerObjectType instanceof ObjectType) {
return null;
}
if (! $this->isObjectType($node->var, new ObjectType('Doctrine\ORM\EntityRepository'))) {
if (! $callerObjectType->isInstanceOf('Doctrine\ORM\EntityRepository')->yes()) {
return null;
}
@ -94,17 +93,6 @@ CODE_SAMPLE
}
$node->var = $this->nodeFactory->createPropertyFetch('this', 'repository');
return $node;
}
private function shouldSkip(MethodCall $methodCall): bool
{
$classLike = $methodCall->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return true;
}
return ! $this->isObjectType($classLike, new ObjectType('Doctrine\ORM\EntityRepository'));
}
}

View File

@ -112,24 +112,27 @@ CODE_SAMPLE
/** @var BooleanAnd $expr */
$expr = $node->cond;
$conditions = $this->nodeRepository->findBooleanAndConditions($expr);
$ifNextReturnClone = $ifNextReturn instanceof Return_
? clone $ifNextReturn
: new Return_();
$isInLoop = $this->contextAnalyzer->isInLoop($node);
$booleanAndConditions = $this->nodeRepository->findBooleanAndConditions($expr);
if (! $ifNextReturn instanceof Return_) {
$this->addNodeAfterNode($node->stmts[0], $node);
return $this->processReplaceIfs($node, $conditions, $ifNextReturnClone);
return $this->processReplaceIfs($node, $booleanAndConditions, new Return_());
}
if ($ifNextReturn instanceof Return_ && $ifNextReturn->expr instanceof BooleanAnd) {
return null;
}
$this->removeNode($ifNextReturn);
$ifNextReturn = $node->stmts[0];
$this->addNodeAfterNode($ifNextReturn, $node);
if (! $isInLoop) {
return $this->processReplaceIfs($node, $conditions, $ifNextReturnClone);
$ifNextReturnClone = $ifNextReturn instanceof Return_
? clone $ifNextReturn
: new Return_();
if (! $this->contextAnalyzer->isInLoop($node)) {
return $this->processReplaceIfs($node, $booleanAndConditions, $ifNextReturnClone);
}
if (! $ifNextReturn instanceof Expression) {
@ -140,7 +143,7 @@ CODE_SAMPLE
$this->addNodeAfterNode(new Return_(), $node);
}
return $this->processReplaceIfs($node, $conditions, $ifNextReturnClone);
return $this->processReplaceIfs($node, $booleanAndConditions, $ifNextReturnClone);
}
/**
@ -234,6 +237,7 @@ CODE_SAMPLE
if (! $this->contextAnalyzer->isInLoop($if)) {
return false;
}
return (bool) $this->betterNodeFinder->findParentTypes($if, [If_::class, Else_::class, ElseIf_::class]);
}

View File

@ -0,0 +1,28 @@
<?php
namespace Rector\EarlyReturn\Tests\Rector\If_\ChangeAndIfToEarlyReturnRector\Fixture;
use PHPStan\Type\CallableType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
final class SkipIfElseAnd
{
public function isClosureAndCallableType(Type $parameterStaticType, Type $argumentStaticType): bool
{
if ($parameterStaticType instanceof CallableType && $this->isClosureObjectType($argumentStaticType)) {
return true;
}
return $argumentStaticType instanceof CallableType && $this->isClosureObjectType($parameterStaticType);
}
private function isClosureObjectType(Type $type): bool
{
if (! $type instanceof ObjectType) {
return false;
}
return $type->getClassName() === 'Closure';
}
}

View File

@ -85,7 +85,12 @@ CODE_SAMPLE
return null;
}
if (! $this->isObjectType($node, new ObjectType('Tester\TestCase'))) {
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $objectType instanceof ObjectType) {
return null;
}
if (! $objectType->isInstanceOf('Tester\TestCase')->yes()) {
return null;
}

View File

@ -65,7 +65,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node, new ObjectType('Tester\Assert'))) {
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $objectType instanceof ObjectType) {
return null;
}
if (! $objectType->isInstanceOf('Tester\Assert')->yes()) {
return null;
}

View File

@ -67,12 +67,20 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isOnClassMethodCall($node, new ObjectType('PHPExcel_Style_Conditional'), 'getCondition')) {
$callerObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node->var);
if (! $callerObjectType instanceof ObjectType) {
return null;
}
if (! $callerObjectType->isInstanceOf('PHPExcel_Style_Conditional')->yes()) {
return null;
}
if (! $this->isName($node->name, 'getCondition')) {
return null;
}
$node->name = new Identifier('getConditions');
$arrayDimFetch = new ArrayDimFetch($node, new LNumber(0));
return new Coalesce($arrayDimFetch, new String_(''));

View File

@ -152,11 +152,11 @@ CODE_SAMPLE
}
/**
* @param Node|Node[] $node
* @param Node[] $node
*/
private function findVariableAssignName($node, string $variableName): ?Node
private function findVariableAssignName(array $node, string $variableName): ?Node
{
return $this->betterNodeFinder->findFirst((array) $node, function (Node $node) use ($variableName): bool {
return $this->betterNodeFinder->findFirst($node, function (Node $node) use ($variableName): bool {
if (! $node instanceof Assign) {
return false;
}

View File

@ -11,6 +11,7 @@ use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\PHPOffice\ValueObject\PHPExcelMethodDefaultValues;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -22,587 +23,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class AddRemovedDefaultValuesRector extends AbstractRector
{
/**
* @var array<string, array<string, array<int, mixed[]>>>
*/
private const METHOD_NAMES_BY_TYPE_WITH_VALUE = [
'PHPExcel' => [
'setHasMacros' => [false],
'setMacrosCode' => [null],
'setMacrosCertificate' => [null],
'setRibbonXMLData' => [null, null],
'setRibbonBinObjects' => [null, null],
'getRibbonBinObjects' => ['all'],
'getSheetByCodeName' => [''],
'createSheet' => [null],
'removeSheetByIndex' => [0],
'getSheet' => [0],
'getSheetByName' => [''],
'setActiveSheetIndex' => [0],
'setActiveSheetIndexByName' => [''],
'getCellXfByIndex' => [0],
'getCellXfByHashCode' => [''],
'cellXfExists' => [null],
'removeCellXfByIndex' => [0],
'getCellStyleXfByIndex' => [0],
'getCellStyleXfByHashCode' => [''],
'removeCellStyleXfByIndex' => [0],
],
'PHPExcel_CalcEngine_Logger' => [
'setWriteDebugLog' => [false],
'setEchoDebugLog' => [false],
],
'PHPExcel_Calculation' => [
'setCalculationCacheEnabled' => [true],
'setLocale' => ['en_us'],
],
'PHPExcel_Calculation_FormulaToken' => [
'setTokenType' => ['PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN'],
'setTokenSubType' => ['PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING'],
],
'PHPExcel_Cell' => [
'setValue' => [null],
'setValueExplicit' => [null, 'PHPExcel_Cell_DataType::TYPE_STRING'],
'setCalculatedValue' => [null],
'setDataType' => ['PHPExcel_Cell_DataType::TYPE_STRING'],
'isInRange' => ['A1:A1'],
'coordinateFromString' => ['A1'],
'absoluteReference' => ['A1'],
'absoluteCoordinate' => ['A1'],
'splitRange' => ['A1:A1'],
'rangeBoundaries' => ['A1:A1'],
'rangeDimension' => ['A1:A1'],
'getRangeBoundaries' => ['A1:A1'],
'columnIndexFromString' => ['A'],
'stringFromColumnIndex' => [0],
'extractAllCellReferencesInRange' => ['A1'],
'setXfIndex' => [0],
],
'PHPExcel_Cell_DataType' => [
'checkString' => [null],
'checkErrorCode' => [null],
],
'PHPExcel_Cell_DataValidation' => [
'setFormula1' => [''],
'setFormula2' => [''],
'setType' => ['PHPExcel_Cell_DataValidation::TYPE_NONE'],
'setErrorStyle' => ['PHPExcel_Cell_DataValidation::STYLE_STOP'],
'setOperator' => [''],
'setAllowBlank' => [false],
'setShowDropDown' => [false],
'setShowInputMessage' => [false],
'setShowErrorMessage' => [false],
'setErrorTitle' => [''],
'setError' => [''],
'setPromptTitle' => [''],
'setPrompt' => [''],
],
'PHPExcel_Cell_DefaultValueBinder' => [
'dataTypeForValue' => [null],
],
'PHPExcel_Cell_Hyperlink' => [
'setUrl' => [''],
'setTooltip' => [''],
],
'PHPExcel_Chart' => [
'setPlotVisibleOnly' => [true],
'setDisplayBlanksAs' => ['0'],
'setTopLeftOffset' => [null, null],
'setBottomRightOffset' => [null, null],
],
'PHPExcel_Chart_DataSeries' => [
'setPlotType' => [''],
'setPlotGrouping' => [null],
'setPlotDirection' => [null],
'setPlotStyle' => [null],
'setSmoothLine' => [true],
],
'PHPExcel_Chart_DataSeriesValues' => [
'setDataType' => ['PHPExcel_Chart_DataSeriesValues::DATASERIES_TYPE_NUMBER'],
'setDataSource' => [null, true],
'setPointMarker' => [null],
'setFormatCode' => [null],
'setDataValues' => [[], true],
],
'PHPExcel_Chart_Layout' => [
'__construct' => [[]],
],
'PHPExcel_Chart_Legend' => [
'setPosition' => ['PHPExcel_Chart_Legend::POSITION_RIGHT'],
'setPositionXL' => ['PHPExcel_Chart_Legend::XL_LEGEND_POSITION_RIGHT'],
'setOverlay' => [false],
],
'PHPExcel_Chart_PlotArea' => [
'__construct' => [null, []],
'setPlotSeries' => [[]],
],
'PHPExcel_Chart_Title' => [
'setCaption' => [null],
],
'PHPExcel_Comment' => [
'setAuthor' => [''],
'setWidth' => ['96pt'],
'setHeight' => ['55.5pt'],
'setMarginLeft' => ['59.25pt'],
'setMarginTop' => ['1.5pt'],
'setVisible' => [false],
'setAlignment' => ['Style\\Alignment::HORIZONTAL_GENERAL'],
],
'PHPExcel_DocumentProperties' => [
'setCreator' => [''],
'setLastModifiedBy' => [''],
'setCreated' => [null],
'setModified' => [null],
'setTitle' => [''],
'setDescription' => [''],
'setSubject' => [''],
'setKeywords' => [''],
'setCategory' => [''],
'setCompany' => [''],
'setManager' => [''],
],
'PHPExcel_DocumentSecurity' => [
'setLockRevision' => [false],
'setLockStructure' => [false],
'setLockWindows' => [false],
'setRevisionsPassword' => ['', false],
'setWorkbookPassword' => ['', false],
],
'PHPExcel_HashTable' => [
'addFromSource' => [null],
'getIndexForHashCode' => [''],
'getByIndex' => [0],
'getByHashCode' => [''],
],
'PHPExcel_IOFactory' => [
'addSearchLocation' => ['', '', ''],
'createReader' => [''],
],
'PHPExcel_NamedRange' => [
'setName' => [null],
'setRange' => [null],
'setLocalOnly' => [false],
],
'PHPExcel_Reader_Abstract' => [
'setReadDataOnly' => [false],
'setReadEmptyCells' => [true],
'setIncludeCharts' => [false],
'setLoadSheetsOnly' => [null],
],
'PHPExcel_Reader_CSV' => [
'setInputEncoding' => ['UTF-8'],
'setDelimiter' => [','],
'setEnclosure' => ['\\'],
'setSheetIndex' => [0],
'setContiguous' => [false],
],
'PHPExcel_Reader_Excel2003XML' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_Excel2007' => [
'parseRichText' => [null],
'boolean' => [null],
],
'PHPExcel_Reader_Excel2007_Chart' => [
'parseRichText' => [null],
],
'PHPExcel_Reader_Excel2007_Theme' => [
'getColourByIndex' => [0],
],
'PHPExcel_Reader_Excel5' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_Gnumeric' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_HTML' => [
'setInputEncoding' => ['ANSI'],
'setSheetIndex' => [0],
],
'PHPExcel_Reader_OOCalc' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_SYLK' => [
'setInputEncoding' => ['ANSI'],
'setSheetIndex' => [0],
],
'PHPExcel_RichText' => [
'createText' => [''],
'createTextRun' => [''],
'setRichTextElements' => [null],
],
'PHPExcel_RichText_TextElement' => [
'setText' => [''],
],
'PHPExcel_Settings' => [
'setLocale' => ['en_us'],
'setLibXmlLoaderOptions' => [null],
],
'PHPExcel_Shared_CodePage' => [
'numberToName' => ['1252'],
],
'PHPExcel_Shared_Date' => [
'excelToDateTimeObject' => [0, null],
'excelToTimestamp' => [0, null],
'PHPToExcel' => [0],
'timestampToExcel' => [0],
'isDateTimeFormatCode' => [''],
'stringToExcel' => [''],
],
'PHPExcel_Shared_Drawing' => [
'pixelsToEMU' => [0],
'EMUToPixels' => [0],
'pixelsToPoints' => [0],
'pointsToPixels' => [0],
'degreesToAngle' => [0],
'angleToDegrees' => [0],
],
'PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer' => [
'setSpgr' => [false],
'setStartCoordinates' => ['A1'],
'setStartOffsetX' => [0],
'setStartOffsetY' => [0],
'setEndCoordinates' => ['A1'],
'setEndOffsetX' => [0],
'setEndOffsetY' => [0],
],
'PHPExcel_Shared_File' => [
'setUseUploadTempDirectory' => [false],
],
'PHPExcel_Shared_Font' => [
'setAutoSizeMethod' => ['PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX'],
'setTrueTypeFontPath' => [''],
'fontSizeToPixels' => ['11'],
'inchSizeToPixels' => ['1'],
'centimeterSizeToPixels' => ['1'],
],
'PHPExcel_Shared_OLE' => [
'localDateToOLE' => [null],
],
'PHPExcel_Shared_PasswordHasher' => [
'hashPassword' => [''],
],
'PHPExcel_Shared_String' => [
'controlCharacterOOXML2PHP' => [''],
'controlCharacterPHP2OOXML' => [''],
'isUTF8' => [''],
'substring' => ['', 0, 0],
'strToUpper' => [''],
'strToLower' => [''],
'strToTitle' => [''],
'strCaseReverse' => [''],
'setDecimalSeparator' => ['.'],
'setThousandsSeparator' => [','],
'setCurrencyCode' => ['$'],
'SYLKtoUTF8' => [''],
],
'PHPExcel_Style' => [
'applyFromArray' => [null, true],
'setConditionalStyles' => [null],
],
'PHPExcel_Style_Alignment' => [
'applyFromArray' => [null],
'setHorizontal' => ['PHPExcel_Style_Alignment::HORIZONTAL_GENERAL'],
'setVertical' => ['PHPExcel_Style_Alignment::VERTICAL_BOTTOM'],
'setTextRotation' => [0],
'setWrapText' => [false],
'setShrinkToFit' => [false],
'setIndent' => [0],
'setReadorder' => [0],
],
'PHPExcel_Style_Border' => [
'applyFromArray' => [null],
'setBorderStyle' => ['PHPExcel_Style_Border::BORDER_NONE'],
],
'PHPExcel_Style_Borders' => [
'applyFromArray' => [null],
'setDiagonalDirection' => ['PHPExcel_Style_Borders::DIAGONAL_NONE'],
],
'PHPExcel_Style_Color' => [
'applyFromArray' => [null],
'setARGB' => ['PHPExcel_Style_Color::COLOR_BLACK'],
'setRGB' => ['000000'],
],
'PHPExcel_Style_Conditional' => [
'setConditionType' => ['PHPExcel_Style_Conditional::CONDITION_NONE'],
'setOperatorType' => ['PHPExcel_Style_Conditional::OPERATOR_NONE'],
'setText' => [null],
'addCondition' => [''],
],
'PHPExcel_Style_Fill' => [
'applyFromArray' => [null],
'setFillType' => ['PHPExcel_Style_Fill::FILL_NONE'],
'setRotation' => [0],
],
'PHPExcel_Style_Font' => [
'applyFromArray' => [null],
'setName' => ['Calibri'],
'setSize' => ['10'],
'setBold' => [false],
'setItalic' => [false],
'setSuperScript' => [false],
'setSubScript' => [false],
'setUnderline' => ['PHPExcel_Style_Font::UNDERLINE_NONE'],
'setStrikethrough' => [false],
],
'PHPExcel_Style_NumberFormat' => [
'applyFromArray' => [null],
'setFormatCode' => ['PHPExcel_Style_NumberFormat::FORMAT_GENERAL'],
'setBuiltInFormatCode' => [0],
'toFormattedString' => ['0', 'PHPExcel_Style_NumberFormat::FORMAT_GENERAL', null],
],
'PHPExcel_Style_Protection' => [
'applyFromArray' => [null],
'setLocked' => ['PHPExcel_Style_Protection::PROTECTION_INHERIT'],
'setHidden' => ['PHPExcel_Style_Protection::PROTECTION_INHERIT'],
],
'PHPExcel_Worksheet' => [
'getChartByIndex' => [null],
'getChartByName' => [''],
'setTitle' => ['Worksheet', true],
'setSheetState' => ['PHPExcel_Worksheet::SHEETSTATE_VISIBLE'],
'setCellValue' => ['A1', null, false],
'setCellValueByColumnAndRow' => [0, '1', null, false],
'setCellValueExplicit' => ['A1', null, 'PHPExcel_Cell_DataType::TYPE_STRING', false],
'setCellValueExplicitByColumnAndRow' => [0, '1', null, 'PHPExcel_Cell_DataType::TYPE_STRING', false],
'getCell' => ['A1', true],
'getCellByColumnAndRow' => [0, '1', true],
'cellExists' => ['A1'],
'cellExistsByColumnAndRow' => [0, '1'],
'getRowDimension' => ['1', true],
'getColumnDimension' => ['A', true],
'getColumnDimensionByColumn' => [0],
'getStyle' => ['A1'],
'getConditionalStyles' => ['A1'],
'conditionalStylesExists' => ['A1'],
'removeConditionalStyles' => ['A1'],
'getStyleByColumnAndRow' => [0, '1', null, null],
'setBreak' => ['A1', 'PHPExcel_Worksheet::BREAK_NONE'],
'setBreakByColumnAndRow' => [0, '1', 'PHPExcel_Worksheet::BREAK_NONE'],
'mergeCells' => ['A1:A1'],
'mergeCellsByColumnAndRow' => [0, '1', 0, '1'],
'unmergeCells' => ['A1:A1'],
'unmergeCellsByColumnAndRow' => [0, '1', 0, '1'],
'setMergeCells' => [[]],
'protectCells' => ['A1', '', false],
'protectCellsByColumnAndRow' => [0, '1', 0, '1', '', false],
'unprotectCells' => ['A1'],
'unprotectCellsByColumnAndRow' => [0, '1', 0, '1', '', false],
'setAutoFilterByColumnAndRow' => [0, '1', 0, '1'],
'freezePane' => [''],
'freezePaneByColumnAndRow' => [0, '1'],
'insertNewRowBefore' => ['1', '1'],
'insertNewColumnBefore' => ['A', '1'],
'insertNewColumnBeforeByIndex' => [0, '1'],
'removeRow' => ['1', '1'],
'removeColumn' => ['A', '1'],
'removeColumnByIndex' => [0, '1'],
'setShowGridlines' => [false],
'setPrintGridlines' => [false],
'setShowRowColHeaders' => [false],
'setShowSummaryBelow' => [true],
'setShowSummaryRight' => [true],
'setComments' => [[]],
'getComment' => ['A1'],
'getCommentByColumnAndRow' => [0, '1'],
'setSelectedCell' => ['A1'],
'setSelectedCells' => ['A1'],
'setSelectedCellByColumnAndRow' => [0, '1'],
'setRightToLeft' => [false],
'fromArray' => [null, null, 'A1', false],
'rangeToArray' => ['A1', null, true, true, false],
'namedRangeToArray' => ['', null, true, true, false],
'getHyperlink' => ['A1'],
'setHyperlink' => ['A1', null],
'hyperlinkExists' => ['A1'],
'getDataValidation' => ['A1'],
'setDataValidation' => ['A1', null],
'dataValidationExists' => ['A1'],
'setCodeName' => [null],
],
'PHPExcel_Worksheet_AutoFilter' => [
'setRange' => [''],
'getColumnByOffset' => [0],
'shiftColumn' => [null, null],
],
'PHPExcel_Worksheet_AutoFilter_Column' => [
'setFilterType' => ['PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_FILTER'],
'setJoin' => ['PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR'],
'setAttributes' => [[]],
'addRule' => [
1 => true,
],
],
'PHPExcel_Worksheet_AutoFilter_Column_Rule' => [
'setRuleType' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_FILTER'],
'setValue' => [''],
'setOperator' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL'],
'setGrouping' => [null],
'setRule' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL', '', null],
],
'PHPExcel_Worksheet_BaseDrawing' => [
'setName' => [''],
'setDescription' => [''],
'setCoordinates' => ['A1'],
'setOffsetX' => [0],
'setOffsetY' => [0],
'setWidth' => [0],
'setHeight' => [0],
'setWidthAndHeight' => [0, 0],
'setResizeProportional' => [true],
'setRotation' => [0],
],
'PHPExcel_Worksheet_CellIterator' => [
'setIterateOnlyExistingCells' => [true],
],
'PHPExcel_Worksheet_ColumnDimension' => [
'setWidth' => ['-1'],
'setAutoSize' => [false],
],
'PHPExcel_Worksheet_Drawing' => [
'setPath' => ['', true],
],
'PHPExcel_Worksheet_Drawing_Shadow' => [
'setVisible' => [false],
'setBlurRadius' => ['6'],
'setDistance' => ['2'],
'setDirection' => [0],
'setAlignment' => [0],
'setAlpha' => [0],
],
'PHPExcel_Worksheet_HeaderFooter' => [
'setDifferentOddEven' => [false],
'setDifferentFirst' => [false],
'setScaleWithDocument' => [true],
'setAlignWithMargins' => [true],
],
'PHPExcel_Worksheet_HeaderFooterDrawing' => [
'setName' => [''],
'setOffsetX' => [0],
'setOffsetY' => [0],
'setWidth' => [0],
'setHeight' => [0],
'setWidthAndHeight' => [0, 0],
'setResizeProportional' => [true],
'setPath' => ['', true],
],
'PHPExcel_Worksheet_MemoryDrawing' => [
'setImageResource' => [null],
'setRenderingFunction' => ['PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT'],
'setMimeType' => ['PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_DEFAULT'],
],
'PHPExcel_Worksheet_PageSetup' => [
'setPaperSize' => ['PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER'],
'setOrientation' => ['PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT'],
'setScale' => ['100', true],
'setFitToPage' => [true],
'setFitToHeight' => ['1', true],
'setFitToWidth' => ['1', true],
'setColumnsToRepeatAtLeft' => [null],
'setColumnsToRepeatAtLeftByStartAndEnd' => ['A', 'A'],
'setRowsToRepeatAtTop' => [null],
'setRowsToRepeatAtTopByStartAndEnd' => ['1', '1'],
'setHorizontalCentered' => [false],
'setVerticalCentered' => [false],
'setFirstPageNumber' => [null],
],
'PHPExcel_Worksheet_Protection' => [
'setSheet' => [false],
'setObjects' => [false],
'setScenarios' => [false],
'setFormatCells' => [false],
'setFormatColumns' => [false],
'setFormatRows' => [false],
'setInsertColumns' => [false],
'setInsertRows' => [false],
'setInsertHyperlinks' => [false],
'setDeleteColumns' => [false],
'setDeleteRows' => [false],
'setSelectLockedCells' => [false],
'setSort' => [false],
'setAutoFilter' => [false],
'setPivotTables' => [false],
'setSelectUnlockedCells' => [false],
'setPassword' => ['', false],
],
'PHPExcel_Worksheet_RowDimension' => [
'setRowHeight' => ['-1'],
'setZeroHeight' => [false],
],
'PHPExcel_Worksheet_SheetView' => [
'setZoomScale' => ['100'],
'setZoomScaleNormal' => ['100'],
'setView' => [null],
],
'PHPExcel_Writer_Abstract' => [
'setIncludeCharts' => [false],
'setPreCalculateFormulas' => [true],
'setUseDiskCaching' => [false, null],
],
'PHPExcel_Writer_CSV' => [
'save' => [null],
'setDelimiter' => [','],
'setEnclosure' => ['\\'],
'setLineEnding' => [PHP_EOL],
'setUseBOM' => [false],
'setIncludeSeparatorLine' => [false],
'setExcelCompatibility' => [false],
'setSheetIndex' => [0],
'writeLine' => [null, null],
],
'PHPExcel_Writer_Excel2007' => [
'getWriterPart' => [''],
'save' => [null],
'setOffice2003Compatibility' => [false],
],
'PHPExcel_Writer_Excel2007_ContentTypes' => [
'getImageMimeType' => [''],
],
'PHPExcel_Writer_Excel2007_StringTable' => [
'writeStringTable' => [null],
'flipStringTable' => [[]],
],
'PHPExcel_Writer_Excel5' => [
'save' => [null],
],
'PHPExcel_Writer_Excel5_Workbook' => [
'writeWorkbook' => [null],
],
'PHPExcel_Writer_Excel5_Worksheet' => [
'writeBIFF8CellRangeAddressFixed' => ['A1'],
],
'PHPExcel_Writer_HTML' => [
'save' => [null],
'setSheetIndex' => [0],
'setGenerateSheetNavigationBlock' => [true],
'setImagesRoot' => ['.'],
'setEmbedImages' => [true],
'setUseInlineCss' => [false],
],
'PHPExcel_Writer_OpenDocument' => [
'getWriterPart' => [''],
'save' => [null],
],
'PHPExcel_Writer_PDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_Core' => [
'setPaperSize' => ['PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER'],
'setOrientation' => ['PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT'],
'setTempDir' => [''],
'prepareForSave' => [null],
],
'PHPExcel_Writer_PDF_DomPDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_mPDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_tcPDF' => [
'save' => [null],
],
];
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Complete removed default values explicitly', [
@ -645,8 +65,13 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
foreach (self::METHOD_NAMES_BY_TYPE_WITH_VALUE as $type => $defaultValuesByMethodName) {
if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType($node, new ObjectType($type))) {
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $objectType instanceof ObjectType) {
return null;
}
foreach (PHPExcelMethodDefaultValues::METHOD_NAMES_BY_TYPE_WITH_VALUE as $type => $defaultValuesByMethodName) {
if (! $objectType->isInstanceOf($type)->yes()) {
continue;
}

View File

@ -80,7 +80,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node->class, new ObjectType('PHPExcel_Cell'))) {
$staticClassObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $staticClassObjectType instanceof ObjectType) {
return null;
}
if (! $staticClassObjectType->isInstanceOf('PHPExcel_Cell')->yes()) {
return null;
}

View File

@ -0,0 +1,589 @@
<?php
declare(strict_types=1);
namespace Rector\PHPOffice\ValueObject;
final class PHPExcelMethodDefaultValues
{
/**
* @var array<string, array<string, array<int, mixed>>>
*/
public const METHOD_NAMES_BY_TYPE_WITH_VALUE = [
'PHPExcel' => [
'setHasMacros' => [false],
'setMacrosCode' => [null],
'setMacrosCertificate' => [null],
'setRibbonXMLData' => [null, null],
'setRibbonBinObjects' => [null, null],
'getRibbonBinObjects' => ['all'],
'getSheetByCodeName' => [''],
'createSheet' => [null],
'removeSheetByIndex' => [0],
'getSheet' => [0],
'getSheetByName' => [''],
'setActiveSheetIndex' => [0],
'setActiveSheetIndexByName' => [''],
'getCellXfByIndex' => [0],
'getCellXfByHashCode' => [''],
'cellXfExists' => [null],
'removeCellXfByIndex' => [0],
'getCellStyleXfByIndex' => [0],
'getCellStyleXfByHashCode' => [''],
'removeCellStyleXfByIndex' => [0],
],
'PHPExcel_CalcEngine_Logger' => [
'setWriteDebugLog' => [false],
'setEchoDebugLog' => [false],
],
'PHPExcel_Calculation' => [
'setCalculationCacheEnabled' => [true],
'setLocale' => ['en_us'],
],
'PHPExcel_Calculation_FormulaToken' => [
'setTokenType' => ['PHPExcel_Calculation_FormulaToken::TOKEN_TYPE_UNKNOWN'],
'setTokenSubType' => ['PHPExcel_Calculation_FormulaToken::TOKEN_SUBTYPE_NOTHING'],
],
'PHPExcel_Cell' => [
'setValue' => [null],
'setValueExplicit' => [null, 'PHPExcel_Cell_DataType::TYPE_STRING'],
'setCalculatedValue' => [null],
'setDataType' => ['PHPExcel_Cell_DataType::TYPE_STRING'],
'isInRange' => ['A1:A1'],
'coordinateFromString' => ['A1'],
'absoluteReference' => ['A1'],
'absoluteCoordinate' => ['A1'],
'splitRange' => ['A1:A1'],
'rangeBoundaries' => ['A1:A1'],
'rangeDimension' => ['A1:A1'],
'getRangeBoundaries' => ['A1:A1'],
'columnIndexFromString' => ['A'],
'stringFromColumnIndex' => [0],
'extractAllCellReferencesInRange' => ['A1'],
'setXfIndex' => [0],
],
'PHPExcel_Cell_DataType' => [
'checkString' => [null],
'checkErrorCode' => [null],
],
'PHPExcel_Cell_DataValidation' => [
'setFormula1' => [''],
'setFormula2' => [''],
'setType' => ['PHPExcel_Cell_DataValidation::TYPE_NONE'],
'setErrorStyle' => ['PHPExcel_Cell_DataValidation::STYLE_STOP'],
'setOperator' => [''],
'setAllowBlank' => [false],
'setShowDropDown' => [false],
'setShowInputMessage' => [false],
'setShowErrorMessage' => [false],
'setErrorTitle' => [''],
'setError' => [''],
'setPromptTitle' => [''],
'setPrompt' => [''],
],
'PHPExcel_Cell_DefaultValueBinder' => [
'dataTypeForValue' => [null],
],
'PHPExcel_Cell_Hyperlink' => [
'setUrl' => [''],
'setTooltip' => [''],
],
'PHPExcel_Chart' => [
'setPlotVisibleOnly' => [true],
'setDisplayBlanksAs' => ['0'],
'setTopLeftOffset' => [null, null],
'setBottomRightOffset' => [null, null],
],
'PHPExcel_Chart_DataSeries' => [
'setPlotType' => [''],
'setPlotGrouping' => [null],
'setPlotDirection' => [null],
'setPlotStyle' => [null],
'setSmoothLine' => [true],
],
'PHPExcel_Chart_DataSeriesValues' => [
'setDataType' => ['PHPExcel_Chart_DataSeriesValues::DATASERIES_TYPE_NUMBER'],
'setDataSource' => [null, true],
'setPointMarker' => [null],
'setFormatCode' => [null],
'setDataValues' => [[], true],
],
'PHPExcel_Chart_Layout' => [
'__construct' => [[]],
],
'PHPExcel_Chart_Legend' => [
'setPosition' => ['PHPExcel_Chart_Legend::POSITION_RIGHT'],
'setPositionXL' => ['PHPExcel_Chart_Legend::XL_LEGEND_POSITION_RIGHT'],
'setOverlay' => [false],
],
'PHPExcel_Chart_PlotArea' => [
'__construct' => [null, []],
'setPlotSeries' => [[]],
],
'PHPExcel_Chart_Title' => [
'setCaption' => [null],
],
'PHPExcel_Comment' => [
'setAuthor' => [''],
'setWidth' => ['96pt'],
'setHeight' => ['55.5pt'],
'setMarginLeft' => ['59.25pt'],
'setMarginTop' => ['1.5pt'],
'setVisible' => [false],
'setAlignment' => ['Style\\Alignment::HORIZONTAL_GENERAL'],
],
'PHPExcel_DocumentProperties' => [
'setCreator' => [''],
'setLastModifiedBy' => [''],
'setCreated' => [null],
'setModified' => [null],
'setTitle' => [''],
'setDescription' => [''],
'setSubject' => [''],
'setKeywords' => [''],
'setCategory' => [''],
'setCompany' => [''],
'setManager' => [''],
],
'PHPExcel_DocumentSecurity' => [
'setLockRevision' => [false],
'setLockStructure' => [false],
'setLockWindows' => [false],
'setRevisionsPassword' => ['', false],
'setWorkbookPassword' => ['', false],
],
'PHPExcel_HashTable' => [
'addFromSource' => [null],
'getIndexForHashCode' => [''],
'getByIndex' => [0],
'getByHashCode' => [''],
],
'PHPExcel_IOFactory' => [
'addSearchLocation' => ['', '', ''],
'createReader' => [''],
],
'PHPExcel_NamedRange' => [
'setName' => [null],
'setRange' => [null],
'setLocalOnly' => [false],
],
'PHPExcel_Reader_Abstract' => [
'setReadDataOnly' => [false],
'setReadEmptyCells' => [true],
'setIncludeCharts' => [false],
'setLoadSheetsOnly' => [null],
],
'PHPExcel_Reader_CSV' => [
'setInputEncoding' => ['UTF-8'],
'setDelimiter' => [','],
'setEnclosure' => ['\\'],
'setSheetIndex' => [0],
'setContiguous' => [false],
],
'PHPExcel_Reader_Excel2003XML' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_Excel2007' => [
'parseRichText' => [null],
'boolean' => [null],
],
'PHPExcel_Reader_Excel2007_Chart' => [
'parseRichText' => [null],
],
'PHPExcel_Reader_Excel2007_Theme' => [
'getColourByIndex' => [0],
],
'PHPExcel_Reader_Excel5' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_Gnumeric' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_HTML' => [
'setInputEncoding' => ['ANSI'],
'setSheetIndex' => [0],
],
'PHPExcel_Reader_OOCalc' => [
'parseRichText' => [''],
],
'PHPExcel_Reader_SYLK' => [
'setInputEncoding' => ['ANSI'],
'setSheetIndex' => [0],
],
'PHPExcel_RichText' => [
'createText' => [''],
'createTextRun' => [''],
'setRichTextElements' => [null],
],
'PHPExcel_RichText_TextElement' => [
'setText' => [''],
],
'PHPExcel_Settings' => [
'setLocale' => ['en_us'],
'setLibXmlLoaderOptions' => [null],
],
'PHPExcel_Shared_CodePage' => [
'numberToName' => ['1252'],
],
'PHPExcel_Shared_Date' => [
'excelToDateTimeObject' => [0, null],
'excelToTimestamp' => [0, null],
'PHPToExcel' => [0],
'timestampToExcel' => [0],
'isDateTimeFormatCode' => [''],
'stringToExcel' => [''],
],
'PHPExcel_Shared_Drawing' => [
'pixelsToEMU' => [0],
'EMUToPixels' => [0],
'pixelsToPoints' => [0],
'pointsToPixels' => [0],
'degreesToAngle' => [0],
'angleToDegrees' => [0],
],
'PHPExcel_Shared_Escher_DgContainer_SpgrContainer_SpContainer' => [
'setSpgr' => [false],
'setStartCoordinates' => ['A1'],
'setStartOffsetX' => [0],
'setStartOffsetY' => [0],
'setEndCoordinates' => ['A1'],
'setEndOffsetX' => [0],
'setEndOffsetY' => [0],
],
'PHPExcel_Shared_File' => [
'setUseUploadTempDirectory' => [false],
],
'PHPExcel_Shared_Font' => [
'setAutoSizeMethod' => ['PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX'],
'setTrueTypeFontPath' => [''],
'fontSizeToPixels' => ['11'],
'inchSizeToPixels' => ['1'],
'centimeterSizeToPixels' => ['1'],
],
'PHPExcel_Shared_OLE' => [
'localDateToOLE' => [null],
],
'PHPExcel_Shared_PasswordHasher' => [
'hashPassword' => [''],
],
'PHPExcel_Shared_String' => [
'controlCharacterOOXML2PHP' => [''],
'controlCharacterPHP2OOXML' => [''],
'isUTF8' => [''],
'substring' => ['', 0, 0],
'strToUpper' => [''],
'strToLower' => [''],
'strToTitle' => [''],
'strCaseReverse' => [''],
'setDecimalSeparator' => ['.'],
'setThousandsSeparator' => [','],
'setCurrencyCode' => ['$'],
'SYLKtoUTF8' => [''],
],
'PHPExcel_Style' => [
'applyFromArray' => [null, true],
'setConditionalStyles' => [null],
],
'PHPExcel_Style_Alignment' => [
'applyFromArray' => [null],
'setHorizontal' => ['PHPExcel_Style_Alignment::HORIZONTAL_GENERAL'],
'setVertical' => ['PHPExcel_Style_Alignment::VERTICAL_BOTTOM'],
'setTextRotation' => [0],
'setWrapText' => [false],
'setShrinkToFit' => [false],
'setIndent' => [0],
'setReadorder' => [0],
],
'PHPExcel_Style_Border' => [
'applyFromArray' => [null],
'setBorderStyle' => ['PHPExcel_Style_Border::BORDER_NONE'],
],
'PHPExcel_Style_Borders' => [
'applyFromArray' => [null],
'setDiagonalDirection' => ['PHPExcel_Style_Borders::DIAGONAL_NONE'],
],
'PHPExcel_Style_Color' => [
'applyFromArray' => [null],
'setARGB' => ['PHPExcel_Style_Color::COLOR_BLACK'],
'setRGB' => ['000000'],
],
'PHPExcel_Style_Conditional' => [
'setConditionType' => ['PHPExcel_Style_Conditional::CONDITION_NONE'],
'setOperatorType' => ['PHPExcel_Style_Conditional::OPERATOR_NONE'],
'setText' => [null],
'addCondition' => [''],
],
'PHPExcel_Style_Fill' => [
'applyFromArray' => [null],
'setFillType' => ['PHPExcel_Style_Fill::FILL_NONE'],
'setRotation' => [0],
],
'PHPExcel_Style_Font' => [
'applyFromArray' => [null],
'setName' => ['Calibri'],
'setSize' => ['10'],
'setBold' => [false],
'setItalic' => [false],
'setSuperScript' => [false],
'setSubScript' => [false],
'setUnderline' => ['PHPExcel_Style_Font::UNDERLINE_NONE'],
'setStrikethrough' => [false],
],
'PHPExcel_Style_NumberFormat' => [
'applyFromArray' => [null],
'setFormatCode' => ['PHPExcel_Style_NumberFormat::FORMAT_GENERAL'],
'setBuiltInFormatCode' => [0],
'toFormattedString' => ['0', 'PHPExcel_Style_NumberFormat::FORMAT_GENERAL', null],
],
'PHPExcel_Style_Protection' => [
'applyFromArray' => [null],
'setLocked' => ['PHPExcel_Style_Protection::PROTECTION_INHERIT'],
'setHidden' => ['PHPExcel_Style_Protection::PROTECTION_INHERIT'],
],
'PHPExcel_Worksheet' => [
'getChartByIndex' => [null],
'getChartByName' => [''],
'setTitle' => ['Worksheet', true],
'setSheetState' => ['PHPExcel_Worksheet::SHEETSTATE_VISIBLE'],
'setCellValue' => ['A1', null, false],
'setCellValueByColumnAndRow' => [0, '1', null, false],
'setCellValueExplicit' => ['A1', null, 'PHPExcel_Cell_DataType::TYPE_STRING', false],
'setCellValueExplicitByColumnAndRow' => [0, '1', null, 'PHPExcel_Cell_DataType::TYPE_STRING', false],
'getCell' => ['A1', true],
'getCellByColumnAndRow' => [0, '1', true],
'cellExists' => ['A1'],
'cellExistsByColumnAndRow' => [0, '1'],
'getRowDimension' => ['1', true],
'getColumnDimension' => ['A', true],
'getColumnDimensionByColumn' => [0],
'getStyle' => ['A1'],
'getConditionalStyles' => ['A1'],
'conditionalStylesExists' => ['A1'],
'removeConditionalStyles' => ['A1'],
'getStyleByColumnAndRow' => [0, '1', null, null],
'setBreak' => ['A1', 'PHPExcel_Worksheet::BREAK_NONE'],
'setBreakByColumnAndRow' => [0, '1', 'PHPExcel_Worksheet::BREAK_NONE'],
'mergeCells' => ['A1:A1'],
'mergeCellsByColumnAndRow' => [0, '1', 0, '1'],
'unmergeCells' => ['A1:A1'],
'unmergeCellsByColumnAndRow' => [0, '1', 0, '1'],
'setMergeCells' => [[]],
'protectCells' => ['A1', '', false],
'protectCellsByColumnAndRow' => [0, '1', 0, '1', '', false],
'unprotectCells' => ['A1'],
'unprotectCellsByColumnAndRow' => [0, '1', 0, '1', '', false],
'setAutoFilterByColumnAndRow' => [0, '1', 0, '1'],
'freezePane' => [''],
'freezePaneByColumnAndRow' => [0, '1'],
'insertNewRowBefore' => ['1', '1'],
'insertNewColumnBefore' => ['A', '1'],
'insertNewColumnBeforeByIndex' => [0, '1'],
'removeRow' => ['1', '1'],
'removeColumn' => ['A', '1'],
'removeColumnByIndex' => [0, '1'],
'setShowGridlines' => [false],
'setPrintGridlines' => [false],
'setShowRowColHeaders' => [false],
'setShowSummaryBelow' => [true],
'setShowSummaryRight' => [true],
'setComments' => [[]],
'getComment' => ['A1'],
'getCommentByColumnAndRow' => [0, '1'],
'setSelectedCell' => ['A1'],
'setSelectedCells' => ['A1'],
'setSelectedCellByColumnAndRow' => [0, '1'],
'setRightToLeft' => [false],
'fromArray' => [null, null, 'A1', false],
'rangeToArray' => ['A1', null, true, true, false],
'namedRangeToArray' => ['', null, true, true, false],
'getHyperlink' => ['A1'],
'setHyperlink' => ['A1', null],
'hyperlinkExists' => ['A1'],
'getDataValidation' => ['A1'],
'setDataValidation' => ['A1', null],
'dataValidationExists' => ['A1'],
'setCodeName' => [null],
],
'PHPExcel_Worksheet_AutoFilter' => [
'setRange' => [''],
'getColumnByOffset' => [0],
'shiftColumn' => [null, null],
],
'PHPExcel_Worksheet_AutoFilter_Column' => [
'setFilterType' => ['PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_FILTERTYPE_FILTER'],
'setJoin' => ['PHPExcel_Worksheet_AutoFilter_Column::AUTOFILTER_COLUMN_JOIN_OR'],
'setAttributes' => [[]],
'addRule' => [
1 => true,
],
],
'PHPExcel_Worksheet_AutoFilter_Column_Rule' => [
'setRuleType' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_RULETYPE_FILTER'],
'setValue' => [''],
'setOperator' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL'],
'setGrouping' => [null],
'setRule' => ['PHPExcel_Worksheet_AutoFilter_Column_Rule::AUTOFILTER_COLUMN_RULE_EQUAL', '', null],
],
'PHPExcel_Worksheet_BaseDrawing' => [
'setName' => [''],
'setDescription' => [''],
'setCoordinates' => ['A1'],
'setOffsetX' => [0],
'setOffsetY' => [0],
'setWidth' => [0],
'setHeight' => [0],
'setWidthAndHeight' => [0, 0],
'setResizeProportional' => [true],
'setRotation' => [0],
],
'PHPExcel_Worksheet_CellIterator' => [
'setIterateOnlyExistingCells' => [true],
],
'PHPExcel_Worksheet_ColumnDimension' => [
'setWidth' => ['-1'],
'setAutoSize' => [false],
],
'PHPExcel_Worksheet_Drawing' => [
'setPath' => ['', true],
],
'PHPExcel_Worksheet_Drawing_Shadow' => [
'setVisible' => [false],
'setBlurRadius' => ['6'],
'setDistance' => ['2'],
'setDirection' => [0],
'setAlignment' => [0],
'setAlpha' => [0],
],
'PHPExcel_Worksheet_HeaderFooter' => [
'setDifferentOddEven' => [false],
'setDifferentFirst' => [false],
'setScaleWithDocument' => [true],
'setAlignWithMargins' => [true],
],
'PHPExcel_Worksheet_HeaderFooterDrawing' => [
'setName' => [''],
'setOffsetX' => [0],
'setOffsetY' => [0],
'setWidth' => [0],
'setHeight' => [0],
'setWidthAndHeight' => [0, 0],
'setResizeProportional' => [true],
'setPath' => ['', true],
],
'PHPExcel_Worksheet_MemoryDrawing' => [
'setImageResource' => [null],
'setRenderingFunction' => ['PHPExcel_Worksheet_MemoryDrawing::RENDERING_DEFAULT'],
'setMimeType' => ['PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_DEFAULT'],
],
'PHPExcel_Worksheet_PageSetup' => [
'setPaperSize' => ['PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER'],
'setOrientation' => ['PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT'],
'setScale' => ['100', true],
'setFitToPage' => [true],
'setFitToHeight' => ['1', true],
'setFitToWidth' => ['1', true],
'setColumnsToRepeatAtLeft' => [null],
'setColumnsToRepeatAtLeftByStartAndEnd' => ['A', 'A'],
'setRowsToRepeatAtTop' => [null],
'setRowsToRepeatAtTopByStartAndEnd' => ['1', '1'],
'setHorizontalCentered' => [false],
'setVerticalCentered' => [false],
'setFirstPageNumber' => [null],
],
'PHPExcel_Worksheet_Protection' => [
'setSheet' => [false],
'setObjects' => [false],
'setScenarios' => [false],
'setFormatCells' => [false],
'setFormatColumns' => [false],
'setFormatRows' => [false],
'setInsertColumns' => [false],
'setInsertRows' => [false],
'setInsertHyperlinks' => [false],
'setDeleteColumns' => [false],
'setDeleteRows' => [false],
'setSelectLockedCells' => [false],
'setSort' => [false],
'setAutoFilter' => [false],
'setPivotTables' => [false],
'setSelectUnlockedCells' => [false],
'setPassword' => ['', false],
],
'PHPExcel_Worksheet_RowDimension' => [
'setRowHeight' => ['-1'],
'setZeroHeight' => [false],
],
'PHPExcel_Worksheet_SheetView' => [
'setZoomScale' => ['100'],
'setZoomScaleNormal' => ['100'],
'setView' => [null],
],
'PHPExcel_Writer_Abstract' => [
'setIncludeCharts' => [false],
'setPreCalculateFormulas' => [true],
'setUseDiskCaching' => [false, null],
],
'PHPExcel_Writer_CSV' => [
'save' => [null],
'setDelimiter' => [','],
'setEnclosure' => ['\\'],
'setLineEnding' => [PHP_EOL],
'setUseBOM' => [false],
'setIncludeSeparatorLine' => [false],
'setExcelCompatibility' => [false],
'setSheetIndex' => [0],
'writeLine' => [null, null],
],
'PHPExcel_Writer_Excel2007' => [
'getWriterPart' => [''],
'save' => [null],
'setOffice2003Compatibility' => [false],
],
'PHPExcel_Writer_Excel2007_ContentTypes' => [
'getImageMimeType' => [''],
],
'PHPExcel_Writer_Excel2007_StringTable' => [
'writeStringTable' => [null],
'flipStringTable' => [[]],
],
'PHPExcel_Writer_Excel5' => [
'save' => [null],
],
'PHPExcel_Writer_Excel5_Workbook' => [
'writeWorkbook' => [null],
],
'PHPExcel_Writer_Excel5_Worksheet' => [
'writeBIFF8CellRangeAddressFixed' => ['A1'],
],
'PHPExcel_Writer_HTML' => [
'save' => [null],
'setSheetIndex' => [0],
'setGenerateSheetNavigationBlock' => [true],
'setImagesRoot' => ['.'],
'setEmbedImages' => [true],
'setUseInlineCss' => [false],
],
'PHPExcel_Writer_OpenDocument' => [
'getWriterPart' => [''],
'save' => [null],
],
'PHPExcel_Writer_PDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_Core' => [
'setPaperSize' => ['PHPExcel_Worksheet_PageSetup::PAPERSIZE_LETTER'],
'setOrientation' => ['PHPExcel_Worksheet_PageSetup::ORIENTATION_DEFAULT'],
'setTempDir' => [''],
'prepareForSave' => [null],
],
'PHPExcel_Writer_PDF_DomPDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_mPDF' => [
'save' => [null],
],
'PHPExcel_Writer_PDF_tcPDF' => [
'save' => [null],
],
];
}

View File

@ -10,6 +10,7 @@ use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\Error;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
@ -146,6 +147,10 @@ final class PhpSpecMocksToPHPUnitMocksRector extends AbstractPhpSpecToPHPUnitRec
$methodsWithWThisMock = $classMocks[$variable];
if ($param->var instanceof Error) {
return null;
}
// single use: "$mock = $this->createMock()"
if (! $this->phpSpecMockCollector->isVariableMockInProperty($param->var)) {
return $this->createNewMockVariableAssign($param, $name);

View File

@ -7,6 +7,7 @@ namespace Rector\Php54\Rector\Break_;
use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Break_;
use PhpParser\Node\Stmt\Continue_;
use PHPStan\Type\Constant\ConstantIntegerType;
@ -101,30 +102,30 @@ CODE_SAMPLE
}
/**
* @param Break_|Continue_ $node
* @param Break_|Continue_ $stmt
*/
private function processVariableNum(Node $node, Variable $numVariable): ?Node
private function processVariableNum(Stmt $stmt, Variable $numVariable): ?Node
{
$staticType = $this->getStaticType($numVariable);
if ($staticType instanceof ConstantType) {
if ($staticType instanceof ConstantIntegerType) {
if ($staticType->getValue() === 0) {
$node->num = null;
return $node;
$stmt->num = null;
return $stmt;
}
if ($staticType->getValue() > 0) {
$node->num = new LNumber($staticType->getValue());
return $node;
$stmt->num = new LNumber($staticType->getValue());
return $stmt;
}
}
return $node;
return $stmt;
}
// remove variable
$node->num = null;
$stmt->num = null;
return null;
}

View File

@ -6,6 +6,7 @@ namespace Rector\Php71\NodeFinder;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\PropertyProperty;
@ -32,14 +33,14 @@ final class EmptyStringDefaultPropertyFinder
/**
* @return PropertyProperty[]
*/
public function find(Node $node): array
public function find(Assign $assign): array
{
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
$classLike = $assign->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return [];
}
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
$className = $assign->getAttribute(AttributeKey::CLASS_NAME);
if (! is_string($className)) {
return [];
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\Php71\Rector\FuncCall;
use function count;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
@ -100,16 +99,16 @@ final class RemoveExtraParametersRector extends AbstractRector
}
}
$reflection = $this->callReflectionResolver->resolveCall($node);
if ($reflection === null) {
$functionReflection = $this->callReflectionResolver->resolveCall($node);
if ($functionReflection === null) {
return true;
}
if ($reflection->getVariants() === []) {
if ($functionReflection->getVariants() === []) {
return true;
}
return $this->hasVariadicParameters($reflection->getVariants());
return $this->hasVariadicParameters($functionReflection->getVariants());
}
/**

View File

@ -94,6 +94,7 @@ final class ClassMethodExternalCallNodeAnalyzer
foreach ($methodCalls as $methodCall) {
$callerType = $this->nodeTypeResolver->resolve($methodCall->var);
if (! $callerType instanceof TypeWithClassName) {
// unable to handle reliably
return $methodCalls;

View File

@ -9,13 +9,12 @@ use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Privatization\NodeAnalyzer\ClassMethodExternalCallNodeAnalyzer;
use Rector\Privatization\VisibilityGuard\ChildClassMethodOverrideGuard;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -30,16 +29,16 @@ final class MakeOnlyUsedByChildrenProtectedRector extends AbstractRector
private $classMethodExternalCallNodeAnalyzer;
/**
* @var FamilyRelationsAnalyzer
* @var ChildClassMethodOverrideGuard
*/
private $familyRelationsAnalyzer;
private $childClassMethodOverrideGuard;
public function __construct(
ClassMethodExternalCallNodeAnalyzer $classMethodExternalCallNodeAnalyzer,
FamilyRelationsAnalyzer $familyRelationsAnalyzer
ChildClassMethodOverrideGuard $childClassMethodOverrideGuard
) {
$this->classMethodExternalCallNodeAnalyzer = $classMethodExternalCallNodeAnalyzer;
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
$this->childClassMethodOverrideGuard = $childClassMethodOverrideGuard;
}
public function getRuleDefinition(): RuleDefinition
@ -107,11 +106,6 @@ CODE_SAMPLE
throw new ShouldNotHappenException();
}
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
throw new ShouldNotHappenException();
}
$externalCalls = $this->classMethodExternalCallNodeAnalyzer->getExternalCalls($node);
if ($externalCalls === []) {
return null;
@ -126,13 +120,18 @@ CODE_SAMPLE
return null;
}
if (! $this->isObjectType($class, new ObjectType($className))) {
$classObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($class);
if (! $classObjectType instanceof ObjectType) {
return null;
}
if (! $classObjectType->isInstanceOf($className)->yes()) {
return null;
}
}
$methodName = $this->getName($node);
if ($this->isOverriddenInChildClass($classReflection, $methodName)) {
if ($this->childClassMethodOverrideGuard->isOverriddenInChildClass($scope, $methodName)) {
return null;
}
@ -166,25 +165,4 @@ CODE_SAMPLE
return ! $classMethod->isPublic();
}
private function isOverriddenInChildClass(ClassReflection $classReflection, string $methodName): bool
{
$childrenClassReflection = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
foreach ($childrenClassReflection as $childClassReflection) {
$singleChildrenClassReflectionHasMethod = $childClassReflection->hasMethod($methodName);
if (! $singleChildrenClassReflectionHasMethod) {
continue;
}
$methodReflection = $childClassReflection->getNativeMethod($methodName);
$methodDeclaringClass = $methodReflection->getDeclaringClass();
if ($methodDeclaringClass->getName() === $childClassReflection->getName()) {
return true;
}
}
return false;
}
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Type\ObjectType;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Privatization\NodeFactory\ClassConstantFetchValueFactory;
@ -119,7 +120,7 @@ CODE_SAMPLE
}
/**
* @param mixed[] $configuration
* @param array<string, mixed[]> $configuration
*/
public function configure(array $configuration): void
{
@ -130,11 +131,16 @@ CODE_SAMPLE
MethodCall $methodCall,
ReplaceStringWithClassConstant $replaceStringWithClassConstant
): ?Arg {
if (! $this->isOnClassMethodCall(
$methodCall,
$replaceStringWithClassConstant->getObjectType(),
$replaceStringWithClassConstant->getMethod()
)) {
$callerObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($methodCall->var);
if (! $callerObjectType instanceof ObjectType) {
return null;
}
if (! $callerObjectType->isInstanceOf($replaceStringWithClassConstant->getClass())->yes()) {
return null;
}
if (! $this->isName($methodCall->name, $replaceStringWithClassConstant->getMethod())) {
return null;
}

View File

@ -4,8 +4,6 @@ declare(strict_types=1);
namespace Rector\Privatization\ValueObject;
use PHPStan\Type\ObjectType;
final class ReplaceStringWithClassConstant
{
/**
@ -39,9 +37,9 @@ final class ReplaceStringWithClassConstant
$this->argPosition = $argPosition;
}
public function getObjectType(): ObjectType
public function getClass(): string
{
return new ObjectType($this->class);
return $this->class;
}
public function getMethod(): string

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Rector\Privatization\VisibilityGuard;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
final class ChildClassMethodOverrideGuard
{
/**
* @var FamilyRelationsAnalyzer
*/
private $familyRelationsAnalyzer;
public function __construct(FamilyRelationsAnalyzer $familyRelationsAnalyzer)
{
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
}
public function isOverriddenInChildClass(Scope $scope, string $methodName): bool
{
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
throw new ShouldNotHappenException();
}
$childrenClassReflection = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
foreach ($childrenClassReflection as $childClassReflection) {
$singleChildrenClassReflectionHasMethod = $childClassReflection->hasMethod($methodName);
if (! $singleChildrenClassReflectionHasMethod) {
continue;
}
$methodReflection = $childClassReflection->getNativeMethod($methodName);
$methodDeclaringClass = $methodReflection->getDeclaringClass();
if ($methodDeclaringClass->getName() === $childClassReflection->getName()) {
return true;
}
}
return false;
}
}

View File

@ -74,7 +74,7 @@ CODE_SAMPLE
public function refactor(Node $node): ?Node
{
foreach ($this->renameClassConstFetches as $renameClassConstFetch) {
if (! $this->isObjectType($node, $renameClassConstFetch->getOldObjectType())) {
if (! $this->isObjectType($node->class, $renameClassConstFetch->getOldObjectType())) {
continue;
}

View File

@ -13,7 +13,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(RenameClassConstFetchRector::class)
->call('configure', [[
RenameClassConstFetchRector::CLASS_CONSTANT_RENAME => ValueObjectInliner::inline([
new RenameClassConstFetch(LocalFormEvents::class, 'PRE_BIND', 'PRE_SUBMIT'),
new RenameClassConstFetch(LocalFormEvents::class, 'BIND', 'SUBMIT'),
new RenameClassConstFetch(LocalFormEvents::class, 'POST_BIND', 'POST_SUBMIT'),
@ -23,7 +22,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
DifferentClass::class,
'NEW_CONSTANT'
),
]),
]]);
};

View File

@ -82,7 +82,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node, new ObjectType('Symfony\Component\Console\Command\Command'))) {
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $objectType instanceof ObjectType) {
return null;
}
if (! $objectType->isInstanceOf('Symfony\Component\Console\Command\Command')->yes()) {
return null;
}
@ -121,7 +126,13 @@ CODE_SAMPLE
if (! $node instanceof StaticCall) {
return null;
}
if (! $this->isObjectType($node->class, new ObjectType('Symfony\Component\Console\Command\Command'))) {
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node->class);
if (! $objectType instanceof ObjectType) {
return null;
}
if (! $objectType->isInstanceOf('Symfony\Component\Console\Command\Command')->yes()) {
return null;
}
@ -144,6 +155,7 @@ CODE_SAMPLE
if (! $node instanceof MethodCall) {
return null;
}
if (! $this->isObjectType($node->var, new ObjectType('Symfony\Component\Console\Command\Command'))) {
return null;
}

View File

@ -9,8 +9,9 @@ use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\ObjectType;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\StringType;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\Rector\AbstractRector;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -27,9 +28,15 @@ final class GetParameterToConstructorInjectionRector extends AbstractRector
*/
private $propertyNaming;
public function __construct(PropertyNaming $propertyNaming)
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(PropertyNaming $propertyNaming, ReflectionProvider $reflectionProvider)
{
$this->propertyNaming = $propertyNaming;
$this->reflectionProvider = $reflectionProvider;
}
public function getRuleDefinition(): RuleDefinition
@ -82,10 +89,13 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType(
$node->var,
new ObjectType('Symfony\Bundle\FrameworkBundle\Controller\Controller')
)) {
$varType = $this->nodeTypeResolver->resolve($node->var);
if (! $varType instanceof TypeWithClassName) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($varType->getClassName());
if (! $classReflection->isSubclassOf('Symfony\Bundle\FrameworkBundle\Controller\Controller')) {
return null;
}
@ -106,7 +116,7 @@ CODE_SAMPLE
return null;
}
$this->addConstructorDependencyToClass($classLike, new StringType(), $propertyName);
$this->propertyAdder->addConstructorDependencyToClass($classLike, new StringType(), $propertyName, 0);
return $this->nodeFactory->createPropertyFetch('this', $propertyName);
}

View File

@ -11,16 +11,15 @@ use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
/**
* Covers https://twig.symfony.com/doc/1.x/deprecated.html#function
@ -30,7 +29,7 @@ use Twig_SimpleFunction;
final class SimpleFunctionAndFilterRector extends AbstractRector
{
/**
* @var array<string, class-string<Twig_SimpleFilter>|class-string<Twig_SimpleFunction>>
* @var array<string, class-string>>
*/
private const OLD_TO_NEW_CLASSES = [
'Twig_Function_Method' => 'Twig_SimpleFunction',
@ -102,12 +101,13 @@ CODE_SAMPLE
return null;
}
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof ClassLike) {
return null;
}
/** @var Scope $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $this->isObjectType($classLike, new ObjectType('Twig_Extension'))) {
/** @var ClassReflection $classReflection */
$classReflection = $scope->getClassReflection();
if (! $classReflection->isSubclassOf('Twig_Extension')) {
return null;
}
@ -125,7 +125,8 @@ CODE_SAMPLE
return null;
}
return $this->processArrayItem($node, $this->getObjectType($node->value));
$newObjectType = $this->nodeTypeResolver->resolve($node->value);
return $this->processArrayItem($node, $newObjectType);
});
return $node;

View File

@ -58,7 +58,7 @@ final class ConsoleExceptionToErrorEventConstantRector extends AbstractRector
public function refactor(Node $node): ?Node
{
if ($node instanceof ClassConstFetch && (
$this->isObjectType($node, $this->consoleEventsObjectType) &&
$this->isObjectType($node->class, $this->consoleEventsObjectType) &&
$this->isName($node->name, 'EXCEPTION'))
) {
return $this->nodeFactory->createClassConstFetch($this->consoleEventsObjectType->getClassName(), 'ERROR');

View File

@ -52,12 +52,14 @@ CODE_SAMPLE
<<<'CODE_SAMPLE'
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormConfigBuilderInterface;
use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
use Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor;
class SomeClass
{
public function run(FormConfigBuilderInterface $builder)
{
$builder->setDataMapper(new \Symfony\Component\Form\Extension\Core\DataMapper\DataMapper(new \Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor()));
$builder->setDataMapper(new DataMapper(new PropertyPathAccessor()));
}
}
CODE_SAMPLE
@ -78,7 +80,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isObjectType($node->var, new ObjectType('Symfony\Component\Form\FormConfigBuilderInterface'))) {
$callerObjectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node->var);
if (! $callerObjectType instanceof ObjectType) {
return null;
}
if (! $callerObjectType->isInstanceOf('Symfony\Component\Form\FormConfigBuilderInterface')->yes()) {
return null;
}

View File

@ -5,7 +5,7 @@ namespace Rector\Symfony5\Tests\Rector\MethodCall\FormBuilderSetDataMapperRector
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormConfigBuilderInterface;
class Fixture
final class Fixture
{
public function run(FormConfigBuilderInterface $builder)
{
@ -20,10 +20,10 @@ namespace Rector\Symfony5\Tests\Rector\MethodCall\FormBuilderSetDataMapperRector
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormConfigBuilderInterface;
class Fixture
final class Fixture
{
public function run(FormConfigBuilderInterface $builder)
{
$builder->setDataMapper(new \Symfony\Component\Form\Extension\Core\DataMapper\DataMapper(new \Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor()));
}
}
}

View File

@ -5,11 +5,11 @@ namespace Rector\Symfony5\Tests\Rector\MethodCall\FormBuilderSetDataMapperRector
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormConfigBuilderInterface;
class SkipCorrectArg
final class SkipCorrectArg
{
public function run(FormConfigBuilderInterface $builder)
{
$propertyPathAccessor = new \Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor();
$builder->setDataMapper(new \Symfony\Component\Form\Extension\Core\DataMapper\DataMapper($propertyPathAccessor));
}
}
}

View File

@ -15,19 +15,10 @@ use PHPStan\Type\UnionType;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\TypeDeclaration\ValueObject\TypeStrictness;
final class CallTypesResolver
{
/**
* @var string
*/
private const STRICTNESS_TYPE_DECLARATION = 'type_declaration';
/**
* @var string
*/
private const STRICTNESS_DOCBLOCK = 'docblock';
/**
* @var NodeTypeResolver
*/
@ -50,7 +41,7 @@ final class CallTypesResolver
*/
public function resolveStrictTypesFromCalls(array $calls): array
{
return $this->resolveTypesFromCalls($calls, self::STRICTNESS_TYPE_DECLARATION);
return $this->resolveTypesFromCalls($calls, TypeStrictness::STRICTNESS_TYPE_DECLARATION);
}
/**
@ -59,7 +50,7 @@ final class CallTypesResolver
*/
public function resolveWeakTypesFromCalls(array $calls): array
{
return $this->resolveTypesFromCalls($calls, self::STRICTNESS_DOCBLOCK);
return $this->resolveTypesFromCalls($calls, TypeStrictness::STRICTNESS_DOCBLOCK);
}
/**
@ -87,7 +78,7 @@ final class CallTypesResolver
private function resolveArgValueType(string $strictnessLevel, Arg $arg): Type
{
if ($strictnessLevel === self::STRICTNESS_TYPE_DECLARATION) {
if ($strictnessLevel === TypeStrictness::STRICTNESS_TYPE_DECLARATION) {
$argValueType = $this->nodeTypeResolver->getNativeType($arg->value);
} else {
$argValueType = $this->nodeTypeResolver->resolve($arg->value);

View File

@ -5,7 +5,9 @@ declare(strict_types=1);
namespace Rector\TypeDeclaration\NodeAnalyzer;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\CallableType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver;
@ -82,7 +84,30 @@ final class ClassMethodParamTypeCompleter
}
$parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type);
if ($this->isClosureAndCallableType($parameterStaticType, $argumentStaticType)) {
return true;
}
// already completed → skip
return $parameterStaticType->equals($argumentStaticType);
}
private function isClosureAndCallableType(Type $parameterStaticType, Type $argumentStaticType): bool
{
if ($parameterStaticType instanceof CallableType && $this->isClosureObjectType($argumentStaticType)) {
return true;
}
return $argumentStaticType instanceof CallableType && $this->isClosureObjectType($parameterStaticType);
}
private function isClosureObjectType(Type $type): bool
{
if (! $type instanceof ObjectType) {
return false;
}
return $type->getClassName() === 'Closure';
}
}

View File

@ -57,7 +57,6 @@ final class SomeClass
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeClass
@ -72,7 +71,6 @@ final class SomeClass
}
}
CODE_SAMPLE
),
]);
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
@ -87,8 +88,13 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
$objectType = $this->nodeTypeResolver->resolveObjectTypeToCompare($node);
if (! $objectType instanceof ObjectType) {
return null;
}
foreach ($this->methodReturnTypes as $methodReturnType) {
if (! $this->isObjectType($node, $methodReturnType->getObjectType())) {
if (! $objectType->isInstanceOf($methodReturnType->getClass())->yes()) {
continue;
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\TypeDeclaration\ValueObject;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
final class AddReturnTypeDeclaration
@ -31,9 +30,9 @@ final class AddReturnTypeDeclaration
$this->returnType = $returnType;
}
public function getObjectType(): ObjectType
public function getClass(): string
{
return new ObjectType($this->class);
return $this->class;
}
public function getMethod(): string

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\ValueObject;
/**
* @enum
*/
final class TypeStrictness
{
/**
* @var string
*/
public const STRICTNESS_TYPE_DECLARATION = 'type_declaration';
/**
* @var string
*/
public const STRICTNESS_DOCBLOCK = 'docblock';
}

View File

@ -0,0 +1,47 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Trait_;
final class ClassTraitClassLike
{
/**
* @param Class_|Trait_ $class
*/
public function run(\PhpParser\Node $class)
{
$this->process($class);
}
public function process($class)
{
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Trait_;
final class ClassTraitClassLike
{
/**
* @param Class_|Trait_ $class
*/
public function run(\PhpParser\Node $class)
{
$this->process($class);
}
public function process(\PhpParser\Node\Stmt\ClassLike $class)
{
}
}
?>

View File

@ -0,0 +1,51 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Nop;
final class NodeOverNodeAbstract
{
public function run()
{
$for = new For_();
$this->provideByType($for);
$string = new String_('hey');
$this->provideByType($string);
}
public function provideByType($node)
{
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Nop;
final class NodeOverNodeAbstract
{
public function run()
{
$for = new For_();
$this->provideByType($for);
$string = new String_('hey');
$this->provideByType($string);
}
public function provideByType(\PhpParser\Node $node)
{
}
}
?>

View File

@ -0,0 +1,19 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
final class SkipCallableToClosure
{
public function run()
{
$someCallback = function () {
return 1;
};
$this->setValue($someCallback);
}
public function setValue(callable $someCallaback)
{
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Source\ClassWithConstant;
final class SkipConstantFetch
{
public function run()
{
$this->setValue(ClassWithConstant::SOME_NAME);
}
public function setValue(string $value)
{
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use Rector\Core\Rector\AbstractRector;
use Rector\PostRector\Rector\AbstractPostRector;
final class SomeRectorPass
{
public function run(AbstractRector $abstractRector, AbstractPostRector $postRector)
{
$this->setValue($abstractRector);
$this->setValue($postRector);
}
private function setValue($rector)
{
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
use Rector\Core\Rector\AbstractRector;
use Rector\PostRector\Rector\AbstractPostRector;
final class SomeRectorPass
{
public function run(AbstractRector $abstractRector, AbstractPostRector $postRector)
{
$this->setValue($abstractRector);
$this->setValue($postRector);
}
private function setValue(\Rector\Core\Contract\Rector\RectorInterface $rector)
{
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Source;
final class ClassWithConstant
{
public const SOME_NAME = 'hey';
}

View File

@ -1,41 +0,0 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture;
class Fixture
{
public function parse()
{
}
public function resolve()
{
}
public function nullable(): array
{
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture;
class Fixture
{
public function parse(): array
{
}
public function resolve(): \SomeType
{
}
public function nullable(): ?\SomeType
{
}
}
?>

View File

@ -1,25 +0,0 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture;
class RemoveReturnType
{
public function clear(): array
{
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture;
class RemoveReturnType
{
public function clear()
{
}
}
?>

View File

@ -2,11 +2,6 @@
declare(strict_types=1);
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\UnionType;
use PHPStan\Type\VoidType;
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
use Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Source\PHPUnitTestCase;
@ -15,34 +10,11 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
return static function (ContainerConfigurator $containerConfigurator): void {
$arrayType = new ArrayType(new MixedType(), new MixedType());
$nullableObjectUnionType = new UnionType([new ObjectType('SomeType'), new NullType()]);
$services = $containerConfigurator->services();
$services->set(AddReturnTypeDeclarationRector::class)
->call('configure', [[
AddReturnTypeDeclarationRector::METHOD_RETURN_TYPES => ValueObjectInliner::inline([
new AddReturnTypeDeclaration(
'Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture\Fixture',
'parse',
$arrayType
),
new AddReturnTypeDeclaration(
'Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture\Fixture',
'resolve',
new ObjectType('SomeType')
),
new AddReturnTypeDeclaration(
'Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture\Fixture',
'nullable',
$nullableObjectUnionType
),
new AddReturnTypeDeclaration(
'Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddReturnTypeDeclarationRector\Fixture\RemoveReturnType',
'clear',
new MixedType()
),
new AddReturnTypeDeclaration(PHPUnitTestCase::class, 'tearDown', new VoidType()),
]),
]]);

View File

@ -11,12 +11,12 @@ use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Broker\FunctionNotFoundException;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\PHPStan\Reflection\TypeToCallReflectionResolver\TypeToCallReflectionResolverRegistry;
@ -126,21 +126,23 @@ final class CallReflectionResolver
*/
private function resolveFunctionCall(FuncCall $funcCall)
{
/** @var Scope|null $scope */
$scope = $funcCall->getAttribute(AttributeKey::SCOPE);
if ($funcCall->name instanceof Name) {
try {
if ($this->reflectionProvider->hasFunction($funcCall->name, $scope)) {
return $this->reflectionProvider->getFunction($funcCall->name, $scope);
} catch (FunctionNotFoundException $functionNotFoundException) {
return null;
}
return null;
}
if (! $scope instanceof Scope) {
return null;
}
return $this->typeToCallReflectionResolverRegistry->resolve($scope->getType($funcCall->name), $scope);
$funcCallNameType = $scope->getType($funcCall->name);
return $this->typeToCallReflectionResolverRegistry->resolve($funcCallNameType, $scope);
}
/**
@ -160,6 +162,10 @@ final class CallReflectionResolver
$classType = $this->nodeTypeResolver->resolve($node instanceof MethodCall ? $node->var : $node->class);
if ($classType instanceof ThisType) {
$classType = $classType->getStaticObjectType();
}
if ($classType instanceof ObjectType) {
if (! $this->reflectionProvider->hasClass($classType->getClassName())) {
return null;

View File

@ -5,11 +5,9 @@ declare(strict_types=1);
namespace Rector\Core\Reflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
use PHPStan\Type\TypeWithClassName;
use ReflectionMethod;
final class ClassMethodReflectionFactory
@ -26,39 +24,26 @@ final class ClassMethodReflectionFactory
public function createFromPHPStanTypeAndMethodName(Type $type, string $methodName): ?ReflectionMethod
{
if ($type instanceof ShortenedObjectType) {
return $this->createReflectionMethodIfExists($type->getFullyQualifiedName(), $methodName);
if ($type instanceof ThisType) {
$type = $type->getStaticObjectType();
}
if ($type instanceof ObjectType) {
return $this->createReflectionMethodIfExists($type->getClassName(), $methodName);
}
if ($type instanceof UnionType || $type instanceof IntersectionType) {
foreach ($type->getTypes() as $unionedType) {
if (! $unionedType instanceof ObjectType) {
continue;
}
$methodReflection = $this->createFromPHPStanTypeAndMethodName($unionedType, $methodName);
if (! $methodReflection instanceof ReflectionMethod) {
continue;
}
return $methodReflection;
}
}
return null;
}
public function createReflectionMethodIfExists(string $class, string $method): ?ReflectionMethod
{
if (! $this->reflectionProvider->hasClass($class)) {
if (! $type instanceof TypeWithClassName) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($class);
return $this->createReflectionMethodIfExists($type, $methodName);
}
public function createReflectionMethodIfExists(
TypeWithClassName $typeWithClassName,
string $method
): ?ReflectionMethod {
if (! $this->reflectionProvider->hasClass($typeWithClassName->getClassName())) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($typeWithClassName->getClassName());
$reflectionClass = $classReflection->getNativeReflection();
if (! $reflectionClass->hasMethod($method)) {

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
if (class_exists('PHPExcel_CalcEngine_Logger')) {
return;
}
final class PHPExcel_CalcEngine_Logger
{
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
if (class_exists('PHPExcel_Cell_DataValidation')) {
return;
}
final class PHPExcel_Cell_DataValidation
{
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
if (class_exists('PHPExcel_Cell')) {
return;
}
final class PHPExcel_Cell
{
}

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
if (class_exists('PHPExcel_Style_Conditional')) {
return;
}
final class PHPExcel_Style_Conditional
{
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Symfony\Component\Form;
if (interface_exists('Symfony\Component\Form\FormConfigBuilderInterface')) {
return;
}
interface FormConfigBuilderInterface
{
}

13
stubs/Tester/Assert.php Normal file
View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Tester;
if (class_exists('Tester\Assert')) {
return;
}
class Assert
{
}