mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-20 23:18:20 +01:00
decopule new VendorLock package
This commit is contained in:
parent
59f82bf804
commit
aaa5ffa8f4
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\Core\NodeContainer\NodeCollector\ParsedNodeCollector;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
abstract class AbstractNodeVendorLockResolver
|
||||
{
|
||||
/**
|
||||
* @var ParsedNodeCollector
|
||||
*/
|
||||
protected $parsedNodeCollector;
|
||||
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
protected $classManipulator;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
protected $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireAbstractNodeVendorLockResolver(
|
||||
ParsedNodeCollector $parsedNodeCollector,
|
||||
ClassManipulator $classManipulator,
|
||||
NodeNameResolver $nodeNameResolver
|
||||
): void {
|
||||
$this->parsedNodeCollector = $parsedNodeCollector;
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
protected function hasParentClassOrImplementsInterface(ClassLike $classLike): bool
|
||||
{
|
||||
if (($classLike instanceof Class_ || $classLike instanceof Interface_) && $classLike->extends) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classLike instanceof Class_) {
|
||||
return (bool) $classLike->implements;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Interface_ $classLike
|
||||
*/
|
||||
protected function isMethodVendorLockedByInterface(ClassLike $classLike, string $methodName): bool
|
||||
{
|
||||
$interfaceNames = $this->classManipulator->getClassLikeNodeParentInterfaceNames($classLike);
|
||||
|
||||
foreach ($interfaceNames as $interfaceName) {
|
||||
if (! interface_exists($interfaceName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$interfaceMethods = get_class_methods($interfaceName);
|
||||
if (! in_array($methodName, $interfaceMethods, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -2,49 +2,19 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker;
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\Core\NodeContainer\NodeCollector\ParsedNodeCollector;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
* @todo decouple to standalone package "packages/vendor-locker"
|
||||
*/
|
||||
final class ReturnNodeVendorLockResolver
|
||||
final class ClassMethodParamVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
private $classManipulator;
|
||||
|
||||
/**
|
||||
* @var ParsedNodeCollector
|
||||
*/
|
||||
private $parsedNodeCollector;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ParsedNodeCollector $parsedNodeCollector,
|
||||
ClassManipulator $classManipulator
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->parsedNodeCollector = $parsedNodeCollector;
|
||||
$this->classManipulator = $classManipulator;
|
||||
}
|
||||
|
||||
public function isVendorLocked(ClassMethod $classMethod): bool
|
||||
public function isVendorLocked(ClassMethod $classMethod, int $paramPosition): bool
|
||||
{
|
||||
/** @var Class_|null $classNode */
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
@ -54,15 +24,20 @@ final class ReturnNodeVendorLockResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
if (! is_string($methodName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
// @todo extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
return $this->isVendorLockedByParentClass($parentClassName, $methodName);
|
||||
$vendorLock = $this->isParentClassVendorLocking($paramPosition, $parentClassName, $methodName);
|
||||
if ($vendorLock !== null) {
|
||||
return $vendorLock;
|
||||
}
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
@ -71,24 +46,10 @@ final class ReturnNodeVendorLockResolver
|
||||
}
|
||||
|
||||
$interfaceNames = $this->classManipulator->getClassLikeNodeParentInterfaceNames($classNode);
|
||||
|
||||
return $this->isVendorLockedByInterface($interfaceNames, $methodName);
|
||||
return $this->isInterfaceParamVendorLockin($interfaceNames, $methodName);
|
||||
}
|
||||
|
||||
private function hasParentClassOrImplementsInterface(ClassLike $classLike): bool
|
||||
{
|
||||
if (($classLike instanceof Class_ || $classLike instanceof Interface_) && $classLike->extends) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classLike instanceof Class_) {
|
||||
return (bool) $classLike->implements;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isVendorLockedByParentClass(string $parentClassName, string $methodName): bool
|
||||
private function isParentClassVendorLocking(int $paramPosition, string $parentClassName, string $methodName): ?bool
|
||||
{
|
||||
$parentClassNode = $this->parsedNodeCollector->findClass($parentClassName);
|
||||
if ($parentClassNode !== null) {
|
||||
@ -96,12 +57,13 @@ final class ReturnNodeVendorLockResolver
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in local scope → it's ok
|
||||
if ($parentMethodNode !== null) {
|
||||
return $parentMethodNode->returnType !== null;
|
||||
// parent method has no type → we cannot change it here
|
||||
return isset($parentMethodNode->params[$paramPosition]) && $parentMethodNode->params[$paramPosition]->type === null;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
|
||||
if (method_exists($parentClassName, $methodName)) {
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in external scope → it's not ok
|
||||
@ -110,13 +72,10 @@ final class ReturnNodeVendorLockResolver
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $interfaceNames
|
||||
*/
|
||||
private function isVendorLockedByInterface(array $interfaceNames, string $methodName): bool
|
||||
private function isInterfaceParamVendorLockin(array $interfaceNames, string $methodName): bool
|
||||
{
|
||||
foreach ($interfaceNames as $interfaceName) {
|
||||
$interface = $this->parsedNodeCollector->findInterface($interfaceName);
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class ClassMethodReturnVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
public function isVendorLocked(ClassMethod $classMethod): bool
|
||||
{
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassOrImplementsInterface($classNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
// @todo extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
return $this->isVendorLockedByParentClass($parentClassName, $methodName);
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classNode instanceof Class_ && ! $classNode instanceof Interface_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isMethodVendorLockedByInterface($classNode, $methodName);
|
||||
}
|
||||
|
||||
private function isVendorLockedByParentClass(string $parentClassName, string $methodName): bool
|
||||
{
|
||||
$parentClassNode = $this->parsedNodeCollector->findClass($parentClassName);
|
||||
if ($parentClassNode !== null) {
|
||||
$parentMethodNode = $parentClassNode->getMethod($methodName);
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in local scope → it's ok
|
||||
if ($parentMethodNode !== null) {
|
||||
return $parentMethodNode->returnType !== null;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
|
||||
if (method_exists($parentClassName, $methodName)) {
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in external scope → it's not ok
|
||||
return true;
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
|
||||
final class ClassMethodVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
/**
|
||||
* Checks for:
|
||||
* - interface required methods
|
||||
* - abstract classes reqired method
|
||||
*
|
||||
* Prevent:
|
||||
* - removing class methods, that breaks the code
|
||||
*/
|
||||
public function isRemovalVendorLocked(ClassMethod $classMethod): bool
|
||||
{
|
||||
$classMethodName = $this->nodeNameResolver->getName($classMethod);
|
||||
if (! is_string($classMethodName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
/** @var Class_|null $class */
|
||||
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($class === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isMethodVendorLockedByInterface($class, $classMethodName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($class->extends === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
$classParents = class_parents($className);
|
||||
foreach ($classParents as $classParent) {
|
||||
if (! class_exists($classParent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClassReflection = new ReflectionClass($classParent);
|
||||
if (! $parentClassReflection->hasMethod($classMethodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodReflection = $parentClassReflection->getMethod($classMethodName);
|
||||
if (! $methodReflection->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class PropertyVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
public function isVendorLocked(Property $property): bool
|
||||
{
|
||||
/** @var Class_|null $classNode */
|
||||
$classNode = $property->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassOrImplementsInterface($classNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string|null $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
if (! is_string($propertyName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
// @todo extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classNode->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
$parentClassProperty = $this->findParentProperty($parentClassName, $propertyName);
|
||||
|
||||
// @todo validate type is conflicting
|
||||
// parent class property in local scope → it's ok
|
||||
if ($parentClassProperty !== null) {
|
||||
return $parentClassProperty->type !== null;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
|
||||
if (property_exists($parentClassName, $propertyName)) {
|
||||
// @todo validate type is conflicting
|
||||
// parent class property in external scope → it's not ok
|
||||
return true;
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function findParentProperty(string $parentClassName, string $propertyName): ?Property
|
||||
{
|
||||
$parentClassNode = $this->parsedNodeCollector->findClass($parentClassName);
|
||||
if ($parentClassNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->classManipulator->getProperty($parentClassNode, $propertyName);
|
||||
}
|
||||
}
|
@ -4,141 +4,60 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\NodeContainer\NodeCollector\ParsedNodeCollector;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnVendorLockResolver;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodVendorLockResolver;
|
||||
use Rector\VendorLocker\NodeVendorLocker\PropertyVendorLockResolver;
|
||||
|
||||
/**
|
||||
* @todo decouple to standalone package "packages/vendor-locker"
|
||||
*/
|
||||
final class VendorLockResolver
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
* @var ClassMethodReturnVendorLockResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
private $classMethodReturnVendorLockResolver;
|
||||
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
* @var ClassMethodParamVendorLockResolver
|
||||
*/
|
||||
private $classManipulator;
|
||||
private $classMethodParamVendorLockResolver;
|
||||
|
||||
/**
|
||||
* @var ParsedNodeCollector
|
||||
* @var PropertyVendorLockResolver
|
||||
*/
|
||||
private $parsedNodeCollector;
|
||||
private $propertyVendorLockResolver;
|
||||
|
||||
/**
|
||||
* @var ReturnNodeVendorLockResolver
|
||||
* @var ClassMethodVendorLockResolver
|
||||
*/
|
||||
private $returnNodeVendorLockResolver;
|
||||
private $classMethodVendorLockResolver;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ParsedNodeCollector $parsedNodeCollector,
|
||||
ClassManipulator $classManipulator,
|
||||
ReturnNodeVendorLockResolver $returnNodeVendorLockResolver
|
||||
ClassMethodReturnVendorLockResolver $classMethodReturnVendorLockResolver,
|
||||
ClassMethodParamVendorLockResolver $classMethodParamVendorLockResolver,
|
||||
PropertyVendorLockResolver $propertyVendorLockResolver,
|
||||
ClassMethodVendorLockResolver $classMethodVendorLockResolver
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->parsedNodeCollector = $parsedNodeCollector;
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->returnNodeVendorLockResolver = $returnNodeVendorLockResolver;
|
||||
$this->classMethodReturnVendorLockResolver = $classMethodReturnVendorLockResolver;
|
||||
$this->classMethodParamVendorLockResolver = $classMethodParamVendorLockResolver;
|
||||
$this->propertyVendorLockResolver = $propertyVendorLockResolver;
|
||||
$this->classMethodVendorLockResolver = $classMethodVendorLockResolver;
|
||||
}
|
||||
|
||||
public function isParamChangeVendorLockedIn(ClassMethod $classMethod, int $paramPosition): bool
|
||||
{
|
||||
/** @var Class_|null $classNode */
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassOrImplementsInterface($classNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
if (! is_string($methodName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
// @todo extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
$vendorLock = $this->isParentClassVendorLocking($paramPosition, $parentClassName, $methodName);
|
||||
if ($vendorLock !== null) {
|
||||
return $vendorLock;
|
||||
}
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classNode instanceof Class_ && ! $classNode instanceof Interface_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$interfaceNames = $this->classManipulator->getClassLikeNodeParentInterfaceNames($classNode);
|
||||
return $this->isInterfaceParamVendorLockin($interfaceNames, $methodName);
|
||||
return $this->classMethodParamVendorLockResolver->isVendorLocked($classMethod, $paramPosition);
|
||||
}
|
||||
|
||||
public function isReturnChangeVendorLockedIn(ClassMethod $classMethod): bool
|
||||
{
|
||||
return $this->returnNodeVendorLockResolver->isVendorLocked($classMethod);
|
||||
return $this->classMethodReturnVendorLockResolver->isVendorLocked($classMethod);
|
||||
}
|
||||
|
||||
public function isPropertyChangeVendorLockedIn(Property $property): bool
|
||||
{
|
||||
/** @var Class_|null $classNode */
|
||||
$classNode = $property->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassOrImplementsInterface($classNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string|null $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
if (! is_string($propertyName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
// @todo extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classNode->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
if ($parentClassName !== null) {
|
||||
$parentClassProperty = $this->findParentProperty($parentClassName, $propertyName);
|
||||
|
||||
// @todo validate type is conflicting
|
||||
// parent class property in local scope → it's ok
|
||||
if ($parentClassProperty !== null) {
|
||||
return $parentClassProperty->type !== null;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
|
||||
if (property_exists($parentClassName, $propertyName)) {
|
||||
// @todo validate type is conflicting
|
||||
// parent class property in external scope → it's not ok
|
||||
return true;
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->propertyVendorLockResolver->isVendorLocked($property);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,158 +70,6 @@ final class VendorLockResolver
|
||||
*/
|
||||
public function isClassMethodRemovalVendorLocked(ClassMethod $classMethod): bool
|
||||
{
|
||||
$classMethodName = $this->nodeNameResolver->getName($classMethod);
|
||||
if (! is_string($classMethodName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
/** @var Class_|null $class */
|
||||
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($class === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isVendorLockedByInterface($class, $classMethodName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($class->extends === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
$classParents = class_parents($className);
|
||||
foreach ($classParents as $classParent) {
|
||||
if (! class_exists($classParent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClassReflection = new ReflectionClass($classParent);
|
||||
if (! $parentClassReflection->hasMethod($classMethodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodReflection = $parentClassReflection->getMethod($classMethodName);
|
||||
if (! $methodReflection->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hasParentClassOrImplementsInterface(Node $classNode): bool
|
||||
{
|
||||
if (($classNode instanceof Class_ || $classNode instanceof Interface_) && $classNode->extends) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classNode instanceof Class_) {
|
||||
return (bool) $classNode->implements;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Until we have getProperty (https://github.com/nikic/PHP-Parser/pull/646)
|
||||
private function getProperty(ClassLike $classLike, string $name)
|
||||
{
|
||||
$lowerName = strtolower($name);
|
||||
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
foreach ($property->props as $propertyProperty) {
|
||||
if ($lowerName !== $propertyProperty->name->toLowerString()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $property;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function findParentProperty(string $parentClassName, string $propertyName): ?Property
|
||||
{
|
||||
$parentClassNode = $this->parsedNodeCollector->findClass($parentClassName);
|
||||
if ($parentClassNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getProperty($parentClassNode, $propertyName);
|
||||
}
|
||||
|
||||
private function isVendorLockedByInterface(Class_ $class, string $classMethodName): bool
|
||||
{
|
||||
// required by interface?
|
||||
foreach ($class->implements as $implement) {
|
||||
$implementedInterfaceName = $this->nodeNameResolver->getName($implement);
|
||||
if (! is_string($implementedInterfaceName)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if (! interface_exists($implementedInterfaceName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$interfaceMethods = get_class_methods($implementedInterfaceName);
|
||||
if (! in_array($classMethodName, $interfaceMethods, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isParentClassVendorLocking(int $paramPosition, string $parentClassName, string $methodName): ?bool
|
||||
{
|
||||
$parentClassNode = $this->parsedNodeCollector->findClass($parentClassName);
|
||||
if ($parentClassNode !== null) {
|
||||
$parentMethodNode = $parentClassNode->getMethod($methodName);
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in local scope → it's ok
|
||||
if ($parentMethodNode !== null) {
|
||||
// parent method has no type → we cannot change it here
|
||||
return isset($parentMethodNode->params[$paramPosition]) && $parentMethodNode->params[$paramPosition]->type === null;
|
||||
}
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
|
||||
if (method_exists($parentClassName, $methodName)) {
|
||||
// @todo validate type is conflicting
|
||||
// parent class method in external scope → it's not ok
|
||||
return true;
|
||||
|
||||
// if not, look for it's parent parent - @todo recursion
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isInterfaceParamVendorLockin(array $interfaceNames, string $methodName): bool
|
||||
{
|
||||
foreach ($interfaceNames as $interfaceName) {
|
||||
$interface = $this->parsedNodeCollector->findInterface($interfaceName);
|
||||
// parent class method in local scope → it's ok
|
||||
// @todo validate type is conflicting
|
||||
if ($interface !== null && $interface->getMethod($methodName) !== null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method_exists($interfaceName, $methodName)) {
|
||||
// parent class method in external scope → it's not ok
|
||||
// @todo validate type is conflicting
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->classMethodVendorLockResolver->isRemovalVendorLocked($classMethod);
|
||||
}
|
||||
}
|
||||
|
@ -200,6 +200,10 @@ final class ClassManipulator
|
||||
return $usedTraits;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo
|
||||
* Waits on https://github.com/nikic/PHP-Parser/pull/646 to be relased
|
||||
*/
|
||||
public function getProperty(ClassLike $classLike, string $name): ?Property
|
||||
{
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user