reflectionProvider = $reflectionProvider; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Refactor MyCLabs enum fetch to Enum const', [new CodeSample(<<<'CODE_SAMPLE' $name = SomeEnum::VALUE()->getKey(); CODE_SAMPLE , <<<'CODE_SAMPLE' $name = SomeEnum::VALUE; CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [MethodCall::class, StaticCall::class]; } /** * @param MethodCall|StaticCall $node */ public function refactor(Node $node) : ?Node { if ($node->name instanceof Expr) { return null; } $enumCaseName = $this->getName($node->name); if ($enumCaseName === null) { return null; } if ($this->shouldOmitEnumCase($enumCaseName)) { return null; } if ($node instanceof MethodCall) { return $this->refactorMethodCall($node, $enumCaseName); } if (!$this->isObjectType($node->class, new ObjectType('MyCLabs\\Enum\\Enum'))) { return null; } $className = $this->getName($node->class); if (!\is_string($className)) { return null; } if (!$this->isEnumConstant($className, $enumCaseName)) { return null; } return $this->nodeFactory->createClassConstFetch($className, $enumCaseName); } public function provideMinPhpVersion() : int { return PhpVersionFeature::ENUM; } private function isEnumConstant(string $className, string $constant) : bool { $classReflection = $this->reflectionProvider->getClass($className); return $classReflection->hasConstant($constant); } private function refactorGetKeyMethodCall(MethodCall $methodCall) : ?ClassConstFetch { if (!$methodCall->var instanceof StaticCall) { return null; } $staticCall = $methodCall->var; $className = $this->getName($staticCall->class); if ($className === null) { return null; } $enumCaseName = $this->getName($staticCall->name); if ($enumCaseName === null) { return null; } if ($this->shouldOmitEnumCase($enumCaseName)) { return null; } return $this->nodeFactory->createClassConstFetch($className, $enumCaseName); } private function refactorGetValueMethodCall(MethodCall $methodCall) : ?PropertyFetch { if (!$methodCall->var instanceof StaticCall) { return null; } $staticCall = $methodCall->var; $className = $this->getName($staticCall->class); if ($className === null) { return null; } $enumCaseName = $this->getName($staticCall->name); if ($enumCaseName === null) { return null; } if ($this->shouldOmitEnumCase($enumCaseName)) { return null; } $enumConstFetch = $this->nodeFactory->createClassConstFetch($className, $enumCaseName); return new PropertyFetch($enumConstFetch, 'value'); } private function refactorEqualsMethodCall(MethodCall $methodCall) : ?Identical { $expr = $this->getNonEnumReturnTypeExpr($methodCall->var); if (!$expr instanceof Expr) { $expr = $this->getValidEnumExpr($methodCall->var); if (!$expr instanceof Expr) { return null; } } $arg = $methodCall->getArgs()[0] ?? null; if (!$arg instanceof Arg) { return null; } $right = $this->getNonEnumReturnTypeExpr($arg->value); if (!$right instanceof Expr) { $right = $this->getValidEnumExpr($arg->value); if (!$right instanceof Expr) { return null; } } return new Identical($expr, $right); } /** * @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $node */ private function isCallerClassEnum($node) : bool { if ($node instanceof StaticCall) { return $this->isObjectType($node->class, new ObjectType('MyCLabs\\Enum\\Enum')); } return $this->isObjectType($node->var, new ObjectType('MyCLabs\\Enum\\Enum')); } /** * @return null|\PhpParser\Node\Expr\ClassConstFetch|\PhpParser\Node\Expr */ private function getNonEnumReturnTypeExpr(Node $node) { if (!$node instanceof StaticCall && !$node instanceof MethodCall) { return null; } if ($this->isCallerClassEnum($node)) { $methodName = $this->getName($node->name); if ($methodName === null) { return null; } if ($node instanceof StaticCall) { $className = $this->getName($node->class); } if ($node instanceof MethodCall) { $className = $this->getName($node->var); } if ($className === null) { return null; } $classReflection = $this->reflectionProvider->getClass($className); // method self::getValidEnumExpr process enum static methods from constants if ($classReflection->hasConstant($methodName)) { return null; } } return $node; } /** * @return null|\PhpParser\Node\Expr\ClassConstFetch|\PhpParser\Node\Expr */ private function getValidEnumExpr(Node $node) { switch (\get_class($node)) { case Variable::class: case PropertyFetch::class: return $this->getPropertyFetchOrVariable($node); case StaticCall::class: return $this->getEnumConstFetch($node); default: return null; } } /** * @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\Variable $expr * @return null|\PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\Variable */ private function getPropertyFetchOrVariable($expr) { if (!$this->isObjectType($expr, new ObjectType('MyCLabs\\Enum\\Enum'))) { return null; } return $expr; } private function getEnumConstFetch(StaticCall $staticCall) : ?\PhpParser\Node\Expr\ClassConstFetch { $className = $this->getName($staticCall->class); if ($className === null) { return null; } $enumCaseName = $this->getName($staticCall->name); if ($enumCaseName === null) { return null; } if ($this->shouldOmitEnumCase($enumCaseName)) { return null; } return $this->nodeFactory->createClassConstFetch($className, $enumCaseName); } /** * @return null|\PhpParser\Node\Expr\ClassConstFetch|\PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\BinaryOp\Identical */ private function refactorMethodCall(MethodCall $methodCall, string $methodName) { if (!$this->isObjectType($methodCall->var, new ObjectType('MyCLabs\\Enum\\Enum'))) { return null; } if ($methodName === 'getKey') { return $this->refactorGetKeyMethodCall($methodCall); } if ($methodName === 'getValue') { return $this->refactorGetValueMethodCall($methodCall); } if ($methodName === 'equals') { return $this->refactorEqualsMethodCall($methodCall); } return null; } private function shouldOmitEnumCase(string $enumCaseName) : bool { return \in_array($enumCaseName, self::ENUM_METHODS, \true); } }