, class-string> */ private const CAST_CLASS_TO_NODE_TYPE = [String_::class => StringType::class, Bool_::class => BooleanType::class, Array_::class => ArrayType::class, Int_::class => IntegerType::class, Object_::class => ObjectType::class, Double::class => FloatType::class]; /** * @readonly * @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer */ private $propertyFetchAnalyzer; /** * @readonly * @var \Rector\Core\Reflection\ReflectionResolver */ private $reflectionResolver; /** * @readonly * @var \Rector\Core\NodeAnalyzer\ExprAnalyzer */ private $exprAnalyzer; /** * @readonly * @var \Rector\Core\PhpParser\AstResolver */ private $astResolver; public function __construct(PropertyFetchAnalyzer $propertyFetchAnalyzer, ReflectionResolver $reflectionResolver, ExprAnalyzer $exprAnalyzer, AstResolver $astResolver) { $this->propertyFetchAnalyzer = $propertyFetchAnalyzer; $this->reflectionResolver = $reflectionResolver; $this->exprAnalyzer = $exprAnalyzer; $this->astResolver = $astResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Removes recasting of the same type', [new CodeSample(<<<'CODE_SAMPLE' $string = ''; $string = (string) $string; $array = []; $array = (array) $array; CODE_SAMPLE , <<<'CODE_SAMPLE' $string = ''; $string = $string; $array = []; $array = $array; CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [Cast::class]; } /** * @param Cast $node */ public function refactor(Node $node) : ?Node { $nodeClass = \get_class($node); if (!isset(self::CAST_CLASS_TO_NODE_TYPE[$nodeClass])) { return null; } $nodeType = $this->getType($node->expr); if ($nodeType instanceof MixedType) { return null; } $sameNodeType = self::CAST_CLASS_TO_NODE_TYPE[$nodeClass]; if (!$nodeType instanceof $sameNodeType) { return null; } if ($this->shouldSkip($node->expr)) { return null; } if ($this->shouldSkipCall($node->expr)) { return null; } return $node->expr; } private function shouldSkipCall(Expr $expr) : bool { if (!$expr instanceof MethodCall && !$expr instanceof StaticCall) { return \false; } $classMethod = $this->astResolver->resolveClassMethodFromCall($expr); if (!$classMethod instanceof ClassMethod) { return \false; } return !$classMethod->returnType instanceof Node; } private function shouldSkip(Expr $expr) : bool { if (!$this->propertyFetchAnalyzer->isPropertyFetch($expr)) { return $this->exprAnalyzer->isNonTypedFromParam($expr); } /** @var PropertyFetch|StaticPropertyFetch $expr */ $phpPropertyReflection = $this->reflectionResolver->resolvePropertyReflectionFromPropertyFetch($expr); if (!$phpPropertyReflection instanceof PhpPropertyReflection) { $propertyType = $expr instanceof StaticPropertyFetch ? $this->nodeTypeResolver->getType($expr->class) : $this->nodeTypeResolver->getType($expr->var); // need to UnionType check due rectify with RecastingRemovalRector + CountOnNullRector // cause add (array) cast on $node->args // on union $node types FuncCall|MethodCall|StaticCall return !$propertyType instanceof UnionType; } $nativeType = $phpPropertyReflection->getNativeType(); return $nativeType instanceof MixedType; } }