decouple ClassMethodVisibilityVendorLockResolver

This commit is contained in:
TomasVotruba 2020-03-29 00:54:19 +01:00
parent 15945fc1bc
commit 58034ac5fa
2 changed files with 114 additions and 88 deletions

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace Rector\VendorLocker\NodeVendorLocker;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class ClassMethodVisibilityVendorLockResolver extends AbstractNodeVendorLockResolver
{
/**
* Checks for:
* - interface required methods
* - abstract classes required method
* - child classes required method
*
* Prevents:
* - changing visibility conflicting with children
*/
public function isParentLockedMethod(ClassMethod $classMethod): bool
{
/** @var string $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($this->isInterfaceMethod($classMethod, $className)) {
return true;
}
/** @var string $methodName */
$methodName = $this->nodeNameResolver->getName($classMethod);
return $this->hasParentMethod($className, $methodName);
}
public function isChildLockedMethod(ClassMethod $classMethod): bool
{
/** @var string $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
/** @var string $methodName */
$methodName = $this->nodeNameResolver->getName($classMethod);
return $this->hasChildMethod($className, $methodName);
}
private function isInterfaceMethod(ClassMethod $classMethod, string $className): bool
{
$interfaceMethods = $this->getInterfaceMethods($className);
return $this->nodeNameResolver->isNames($classMethod, $interfaceMethods);
}
/**
* @return string[]
*/
private function getInterfaceMethods(string $className): array
{
$interfaces = class_implements($className);
$interfaceMethods = [];
foreach ($interfaces as $interface) {
$interfaceMethods = array_merge($interfaceMethods, get_class_methods($interface));
}
return $interfaceMethods;
}
private function hasParentMethod(string $className, string $methodName): bool
{
$parentClasses = class_parents($className);
foreach ($parentClasses as $parentClass) {
if (! method_exists($parentClass, $methodName)) {
continue;
}
return true;
}
return false;
}
private function hasChildMethod(string $desiredClassName, string $methodName): bool
{
foreach (get_declared_classes() as $className) {
if ($className === $desiredClassName) {
continue;
}
if (! is_a($className, $desiredClassName, true)) {
continue;
}
if (method_exists($className, $methodName)) {
return true;
}
}
return false;
}
}

View File

@ -18,6 +18,7 @@ use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeCollector\NodeFinder\MethodCallParsedNodesFinder;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodVisibilityVendorLockResolver;
use ReflectionMethod;
/**
@ -30,9 +31,17 @@ final class PrivatizeLocalOnlyMethodRector extends AbstractRector
*/
private $methodCallParsedNodesFinder;
public function __construct(MethodCallParsedNodesFinder $methodCallParsedNodesFinder)
{
/**
* @var ClassMethodVisibilityVendorLockResolver
*/
private $classMethodVisibilityVendorLockResolver;
public function __construct(
MethodCallParsedNodesFinder $methodCallParsedNodesFinder,
ClassMethodVisibilityVendorLockResolver $classMethodVisibilityVendorLockResolver
) {
$this->methodCallParsedNodesFinder = $methodCallParsedNodesFinder;
$this->classMethodVisibilityVendorLockResolver = $classMethodVisibilityVendorLockResolver;
}
public function getDefinition(): RectorDefinition
@ -123,13 +132,11 @@ PHP
}
// is interface required method? skip it
/** @var string $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($this->isParentLockedMethod($classMethod, $className)) {
if ($this->classMethodVisibilityVendorLockResolver->isParentLockedMethod($classMethod)) {
return true;
}
if ($this->isChildLockedMethod($classMethod, $className)) {
if ($this->classMethodVisibilityVendorLockResolver->isChildLockedMethod($classMethod)) {
return true;
}
@ -185,69 +192,6 @@ PHP
return false;
}
/**
* @todo move to method visibility vendor lockin
*/
private function isParentLockedMethod(ClassMethod $classMethod, string $className): bool
{
$methodName = $this->getName($classMethod);
if ($this->isInterfaceMethod($classMethod, $className)) {
return true;
}
return $this->hasParentMethod($className, $methodName);
}
/**
* @todo move to vendor lockin
*/
private function isInterfaceMethod(ClassMethod $classMethod, string $className): bool
{
$interfaceMethods = $this->getInterfaceMethods($className);
return $this->isNames($classMethod, $interfaceMethods);
}
/**
* @return string[]
*/
private function getInterfaceMethods(string $className): array
{
$interfaces = class_implements($className);
$interfaceMethods = [];
foreach ($interfaces as $interface) {
$interfaceMethods = array_merge($interfaceMethods, get_class_methods($interface));
}
return $interfaceMethods;
}
private function hasParentMethod(string $className, string $methodName)
{
$parentClasses = class_parents($className);
foreach ($parentClasses as $parentClass) {
if (! method_exists($parentClass, $methodName)) {
continue;
}
return true;
}
return false;
}
/**
* @todo move to vendor lockin
*/
private function isChildLockedMethod(ClassMethod $classMethod, string $className): bool
{
$methodName = $this->getName($classMethod);
return $this->hasChildMethod($className, $methodName);
}
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
{
if ($classMethod->isPrivate()) {
@ -271,25 +215,6 @@ PHP
return $this->isNames($classMethod, ['create', 'create*']);
}
private function hasChildMethod(string $desiredClassName, string $methodName): bool
{
foreach (get_declared_classes() as $className) {
if ($className === $desiredClassName) {
continue;
}
if (! is_a($className, $desiredClassName, true)) {
continue;
}
if (method_exists($className, $methodName)) {
return true;
}
}
return false;
}
/**
* @param StaticCall[]|MethodCall[]|ArrayCallable[] $methodCalls
*/