mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 05:18:18 +01:00
Make use of static reflection from PHPStan (#5665)
* break test without autoload * PHPStanServiceFactory - add tests directory as analysed paths * use ReflectionProvider instead of class_exists * use native reflections for annotation reader * use native class and method reflections * add RectorBetterReflectionSourceLocatorFactory * add DynamicSourceLocator that is shared by PHPStan and Rector * use ReflectoinProvider instead of class_exists() * resolve native ClassMethodReflection * make AnonymousFunctionFactory use reflection * [DowngradePHP74] Improve DowngradeCovariantReturnTypeRector * phsptan: avoid ClassReflection * ProcessCommand: add file infos to source locator * [DeadCode] Skip RemoveUnusedPublicMethodRector for test * remove autolaod from tests * misc * remove is_a() * [CI] enable only few dirs to avoid fails * misc * [ci-review] Rector Rectify * [ci-review] Rector Rectify Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
parent
c480e0468c
commit
4909e63a1d
7
.github/workflows/rector_ci.yaml
vendored
7
.github/workflows/rector_ci.yaml
vendored
@ -21,7 +21,11 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
directories:
|
||||
- rules
|
||||
#- rules
|
||||
- rules/naming
|
||||
- rules/privatization
|
||||
- rules/code-quality
|
||||
- rules/php74
|
||||
- packages
|
||||
- src
|
||||
- tests
|
||||
@ -54,6 +58,7 @@ jobs:
|
||||
|
||||
## First run Rector - here can't be --dry-run !!! it would stop the job with it and not commit anything in the future
|
||||
- run: bin/rector rectify ${{ matrix.directories }} --ansi --no-progress-bar
|
||||
|
||||
- run: vendor/bin/ecs check --match-git-diff --fix --ansi
|
||||
|
||||
# see https://github.com/EndBug/add-and-commit
|
||||
|
@ -37,7 +37,7 @@
|
||||
"nette/utils": "^3.2",
|
||||
"nikic/php-parser": "^4.10.4",
|
||||
"phpstan/phpdoc-parser": "^0.4.9",
|
||||
"phpstan/phpstan": "^0.12.76",
|
||||
"phpstan/phpstan": "^0.12.79",
|
||||
"phpstan/phpstan-phpunit": "^0.12.17",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"sebastian/diff": "^4.0.4",
|
||||
@ -186,12 +186,11 @@
|
||||
"rules/autodiscovery/tests/Rector/FileNode/MoveServicesBySuffixToDirectoryRector/Expected",
|
||||
"rules/cakephp/tests/Rector/FileWithoutNamespace/ImplicitShortClassNameUseStatementRector/Source",
|
||||
"rules/cakephp/tests/Rector/Namespace_/AppUsesStaticCallToUseStatementRector/Source",
|
||||
"rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source",
|
||||
"rules/renaming/tests/Rector/FileWithoutNamespace/PseudoNamespaceToNamespaceRector/Source",
|
||||
"rules/symfony4/tests/Rector/MethodCall/ContainerGetToConstructorInjectionRector/Source"
|
||||
],
|
||||
"files": [
|
||||
"rules/type-declaration/tests/Rector/ClassMethod/ReturnTypeFromStrictTypedCallRector/Source/external_bool_function.php",
|
||||
"vendor/nette/forms/src/Forms/Controls/SubmitButton.php",
|
||||
"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",
|
||||
|
@ -10,11 +10,13 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\Php\PhpPropertyReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\DoctrineAnnotationGenerated\PhpDocNode\ConstantReferenceIdentifierRestorer;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use Throwable;
|
||||
@ -41,14 +43,21 @@ final class NodeAnnotationReader
|
||||
*/
|
||||
private $constantReferenceIdentifierRestorer;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ConstantReferenceIdentifierRestorer $constantReferenceIdentifierRestorer,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
Reader $reader
|
||||
Reader $reader,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->reader = $reader;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->constantReferenceIdentifierRestorer = $constantReferenceIdentifierRestorer;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function readAnnotation(Node $node, string $annotationClass): ?object
|
||||
@ -71,12 +80,13 @@ final class NodeAnnotationReader
|
||||
public function readClassAnnotation(Class_ $class, string $annotationClassName): ?object
|
||||
{
|
||||
$classReflection = $this->createClassReflectionFromNode($class);
|
||||
$nativeClassReflection = $classReflection->getNativeReflection();
|
||||
|
||||
try {
|
||||
// covers cases like https://github.com/rectorphp/rector/issues/3046
|
||||
|
||||
/** @var object[] $classAnnotations */
|
||||
$classAnnotations = $this->reader->getClassAnnotations($classReflection);
|
||||
$classAnnotations = $this->reader->getClassAnnotations($nativeClassReflection);
|
||||
return $this->matchNextAnnotation($classAnnotations, $annotationClassName, $class);
|
||||
} catch (AnnotationException $annotationException) {
|
||||
// unable to load
|
||||
@ -86,16 +96,16 @@ final class NodeAnnotationReader
|
||||
|
||||
public function readPropertyAnnotation(Property $property, string $annotationClassName): ?object
|
||||
{
|
||||
$propertyReflection = $this->createPropertyReflectionFromPropertyNode($property);
|
||||
if (! $propertyReflection instanceof ReflectionProperty) {
|
||||
return null;
|
||||
$reflectionProperty = $this->getNativePropertyReflection($property);
|
||||
if (! $reflectionProperty instanceof ReflectionProperty) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
try {
|
||||
// covers cases like https://github.com/rectorphp/rector/issues/3046
|
||||
|
||||
/** @var object[] $propertyAnnotations */
|
||||
$propertyAnnotations = $this->reader->getPropertyAnnotations($propertyReflection);
|
||||
$propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
|
||||
return $this->matchNextAnnotation($propertyAnnotations, $annotationClassName, $property);
|
||||
} catch (AnnotationException $annotationException) {
|
||||
// unable to load
|
||||
@ -111,13 +121,14 @@ final class NodeAnnotationReader
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
$reflectionMethod = new ReflectionMethod($className, $methodName);
|
||||
$reflectionMethod = $this->resolveNativeClassMethodReflection($className, $methodName);
|
||||
|
||||
try {
|
||||
// covers cases like https://github.com/rectorphp/rector/issues/3046
|
||||
|
||||
/** @var object[] $methodAnnotations */
|
||||
$methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
|
||||
|
||||
foreach ($methodAnnotations as $methodAnnotation) {
|
||||
if (! is_a($methodAnnotation, $annotationClassName, true)) {
|
||||
continue;
|
||||
@ -141,14 +152,13 @@ final class NodeAnnotationReader
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createClassReflectionFromNode(Class_ $class): ReflectionClass
|
||||
private function createClassReflectionFromNode(Class_ $class): ClassReflection
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $this->nodeNameResolver->getName($class);
|
||||
|
||||
// covers cases like https://github.com/rectorphp/rector/issues/3230#issuecomment-683317288
|
||||
|
||||
return new ReflectionClass($className);
|
||||
return $this->reflectionProvider->getClass($className);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,7 +185,7 @@ final class NodeAnnotationReader
|
||||
return null;
|
||||
}
|
||||
|
||||
private function createPropertyReflectionFromPropertyNode(Property $property): ?ReflectionProperty
|
||||
private function getNativePropertyReflection(Property $property): ?ReflectionProperty
|
||||
{
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
@ -186,16 +196,35 @@ final class NodeAnnotationReader
|
||||
// probably fresh node
|
||||
return null;
|
||||
}
|
||||
if (! ClassExistenceStaticHelper::doesClassLikeExist($className)) {
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
// probably fresh node
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new ReflectionProperty($className, $propertyName);
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
$scope = $property->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$propertyReflection = $classReflection->getProperty($propertyName, $scope);
|
||||
if ($propertyReflection instanceof PhpPropertyReflection) {
|
||||
return $propertyReflection->getNativeReflection();
|
||||
}
|
||||
} catch (Throwable $throwable) {
|
||||
// in case of PHPUnit property or just-added property
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveNativeClassMethodReflection(string $className, string $methodName): ReflectionMethod
|
||||
{
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
$reflectionClass = $classReflection->getNativeReflection();
|
||||
|
||||
return $reflectionClass->getMethod($methodName);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ namespace Rector\BetterPhpDocParser\PhpDocNodeFactory;
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\PhpDocParser\Parser\TokenIterator;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\BetterPhpDocParser\Annotation\AnnotationItemsResolver;
|
||||
use Rector\BetterPhpDocParser\AnnotationReader\NodeAnnotationReader;
|
||||
@ -56,6 +57,11 @@ abstract class AbstractPhpDocNodeFactory
|
||||
*/
|
||||
private $objectTypeSpecifier;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
@ -63,12 +69,14 @@ abstract class AbstractPhpDocNodeFactory
|
||||
NodeAnnotationReader $nodeAnnotationReader,
|
||||
AnnotationContentResolver $annotationContentResolver,
|
||||
AnnotationItemsResolver $annotationItemsResolver,
|
||||
ObjectTypeSpecifier $objectTypeSpecifier
|
||||
ObjectTypeSpecifier $objectTypeSpecifier,
|
||||
ReflectionProvider $reflectionProvider
|
||||
): void {
|
||||
$this->nodeAnnotationReader = $nodeAnnotationReader;
|
||||
$this->annotationContentResolver = $annotationContentResolver;
|
||||
$this->annotationItemsResolver = $annotationItemsResolver;
|
||||
$this->objectTypeSpecifier = $objectTypeSpecifier;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
protected function resolveContentFromTokenIterator(TokenIterator $tokenIterator): string
|
||||
@ -79,12 +87,12 @@ abstract class AbstractPhpDocNodeFactory
|
||||
protected function resolveFqnTargetEntity(string $targetEntity, Node $node): string
|
||||
{
|
||||
$targetEntity = $this->getCleanedUpTargetEntity($targetEntity);
|
||||
if (class_exists($targetEntity)) {
|
||||
if ($this->reflectionProvider->hasClass($targetEntity)) {
|
||||
return $targetEntity;
|
||||
}
|
||||
|
||||
$namespacedTargetEntity = $node->getAttribute(AttributeKey::NAMESPACE_NAME) . '\\' . $targetEntity;
|
||||
if (class_exists($namespacedTargetEntity)) {
|
||||
if ($this->reflectionProvider->hasClass($namespacedTargetEntity)) {
|
||||
return $namespacedTargetEntity;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
@ -20,6 +21,16 @@ final class ClassAnnotationMatcher
|
||||
*/
|
||||
private $fullyQualifiedNameByHash = [];
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function resolveTagFullyQualifiedName(string $tag, Node $node): string
|
||||
{
|
||||
$uniqueHash = $tag . spl_object_hash($node);
|
||||
@ -48,7 +59,7 @@ final class ClassAnnotationMatcher
|
||||
$namespace = $node->getAttribute(AttributeKey::NAMESPACE_NAME);
|
||||
if ($namespace !== null) {
|
||||
$namespacedTag = $namespace . '\\' . $tag;
|
||||
if (class_exists($namespacedTag)) {
|
||||
if ($this->reflectionProvider->hasClass($namespacedTag)) {
|
||||
return $namespacedTag;
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,7 @@ final class MultilineSpaceFormatPreserver
|
||||
|
||||
public function resolveCurrentPhpDocNodeText(Node $node): ?string
|
||||
{
|
||||
if ($node instanceof PhpDocTagNode &&
|
||||
property_exists($node->value, 'description')
|
||||
) {
|
||||
if ($node instanceof PhpDocTagNode && property_exists($node->value, 'description')) {
|
||||
return $node->value->description;
|
||||
}
|
||||
|
||||
|
@ -31,16 +31,16 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
|
||||
*/
|
||||
protected $tagValueNodeConfiguration;
|
||||
|
||||
/**
|
||||
* @var ArrayPartPhpDocTagPrinter
|
||||
*/
|
||||
protected $arrayPartPhpDocTagPrinter;
|
||||
|
||||
/**
|
||||
* @var TagValueNodePrinter
|
||||
*/
|
||||
protected $tagValueNodePrinter;
|
||||
|
||||
/**
|
||||
* @var ArrayPartPhpDocTagPrinter
|
||||
*/
|
||||
private $arrayPartPhpDocTagPrinter;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $items
|
||||
*/
|
||||
|
@ -9,7 +9,6 @@ use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\SilentKeyNodeInterface;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\AbstractTagValueNode;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
/**
|
||||
* @see \Rector\BetterPhpDocParser\Tests\PhpDocParser\TagValueNodeReprint\TagValueNodeReprintTest
|
||||
@ -19,7 +18,7 @@ final class SymfonyRouteTagValueNode extends AbstractTagValueNode implements Sho
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CLASS_NAME = Route::class;
|
||||
public const CLASS_NAME = 'Symfony\Component\Routing\Annotation\Route';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
@ -14,8 +14,5 @@ class ApiFilter
|
||||
{
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if(! class_exists($options['value'])) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,88 +4,63 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\FamilyTree\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
|
||||
|
||||
final class ClassChildAnalyzer
|
||||
{
|
||||
public function hasChildClassConstructor(Class_ $class): bool
|
||||
{
|
||||
$childClasses = $this->getChildClasses($class);
|
||||
|
||||
foreach ($childClasses as $childClass) {
|
||||
if (! class_exists($childClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$reflectionClass = new ReflectionClass($childClass);
|
||||
$constructorReflectionMethod = $reflectionClass->getConstructor();
|
||||
if (! $constructorReflectionMethod instanceof ReflectionMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($constructorReflectionMethod->class !== $childClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasParentClassConstructor(Class_ $class): bool
|
||||
{
|
||||
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string[] $classParents */
|
||||
$classParents = (array) class_parents($className);
|
||||
|
||||
foreach ($classParents as $classParent) {
|
||||
$parentReflectionClass = new ReflectionClass($classParent);
|
||||
$constructMethodReflection = $parentReflectionClass->getConstructor();
|
||||
if (! $constructMethodReflection instanceof ReflectionMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($constructMethodReflection->class !== $classParent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string[]
|
||||
* @var FamilyRelationsAnalyzer
|
||||
*/
|
||||
private function getChildClasses(Class_ $class): array
|
||||
private $familyRelationsAnalyzer;
|
||||
|
||||
public function __construct(FamilyRelationsAnalyzer $familyRelationsAnalyzer)
|
||||
{
|
||||
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return [];
|
||||
}
|
||||
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
|
||||
}
|
||||
|
||||
$childClasses = [];
|
||||
foreach (get_declared_classes() as $declaredClass) {
|
||||
if (! is_a($declaredClass, $className, true)) {
|
||||
public function hasChildClassMethod(ClassReflection $classReflection, string $methodName): bool
|
||||
{
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
|
||||
foreach ($childrenClassReflections as $childClassReflection) {
|
||||
if (! $childClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($declaredClass === $className) {
|
||||
$constructorReflectionMethod = $childClassReflection->getNativeMethod($methodName);
|
||||
if (! $constructorReflectionMethod instanceof PhpMethodReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childClasses[] = $declaredClass;
|
||||
$methodDeclaringClassReflection = $constructorReflectionMethod->getDeclaringClass();
|
||||
if ($methodDeclaringClassReflection->getName() === $childClassReflection->getName()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return $childClasses;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasParentClassMethod(ClassReflection $classReflection, string $methodName): bool
|
||||
{
|
||||
foreach ($classReflection->getParents() as $parentClassReflections) {
|
||||
if (! $parentClassReflections->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$constructMethodReflection = $parentClassReflections->getNativeMethod($methodName);
|
||||
if (! $constructMethodReflection instanceof PhpMethodReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodDeclaringMethodClass = $constructMethodReflection->getDeclaringClass();
|
||||
if ($methodDeclaringMethodClass->getName() === $parentClassReflections->getName()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ namespace Rector\FamilyTree\NodeAnalyzer;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
@ -54,16 +57,26 @@ final class PropertyUsageAnalyzer
|
||||
return false;
|
||||
}
|
||||
|
||||
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classLike instanceof Class_ && $classLike->isFinal()) {
|
||||
$scope = $property->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($classReflection->isClass() && $classReflection->isFinal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
$childrenClassNames = $this->familyRelationsAnalyzer->getChildrenOfClass($className);
|
||||
foreach ($childrenClassNames as $childClassName) {
|
||||
$childClass = $this->nodeRepository->findClass($childClassName);
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
|
||||
foreach ($childrenClassReflections as $childClassReflection) {
|
||||
$childClass = $this->nodeRepository->findClass($childClassReflection->getName());
|
||||
if (! $childClass instanceof Class_) {
|
||||
continue;
|
||||
}
|
||||
|
@ -4,43 +4,46 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\FamilyTree\Reflection;
|
||||
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
|
||||
final class FamilyRelationsAnalyzer
|
||||
{
|
||||
/**
|
||||
* @return class-string[]
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
public function getChildrenOfClass(string $parentClass): array
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var PrivatesAccessor
|
||||
*/
|
||||
private $privatesAccessor;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider, PrivatesAccessor $privatesAccessor)
|
||||
{
|
||||
$childrenClasses = [];
|
||||
foreach (get_declared_classes() as $declaredClass) {
|
||||
if ($declaredClass === $parentClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_a($declaredClass, $parentClass, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childrenClasses[] = $declaredClass;
|
||||
}
|
||||
|
||||
return $childrenClasses;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->privatesAccessor = $privatesAccessor;
|
||||
}
|
||||
|
||||
public function isParentClass(string $class): bool
|
||||
/**
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
public function getChildrenOfClassReflection(ClassReflection $desiredClassReflection): array
|
||||
{
|
||||
foreach (get_declared_classes() as $declaredClass) {
|
||||
if ($declaredClass === $class) {
|
||||
/** @var ClassReflection[] $classReflections */
|
||||
$classReflections = $this->privatesAccessor->getPrivateProperty($this->reflectionProvider, 'classes');
|
||||
|
||||
$childrenClassReflections = [];
|
||||
|
||||
foreach ($classReflections as $classReflection) {
|
||||
if (! $classReflection->isSubclassOf($desiredClassReflection->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_a($declaredClass, $class, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
$childrenClassReflections[] = $classReflection;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $childrenClassReflections;
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,9 @@ use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
@ -51,7 +53,7 @@ use ReflectionMethod;
|
||||
final class NodeRepository
|
||||
{
|
||||
/**
|
||||
* @var array<string, ClassMethod[]>
|
||||
* @var array<class-string, ClassMethod[]>
|
||||
*/
|
||||
private $classMethodsByType = [];
|
||||
|
||||
@ -66,13 +68,13 @@ final class NodeRepository
|
||||
private $funcCallsByName = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<array<MethodCall|StaticCall>>>
|
||||
* @var array<class-string, array<array<MethodCall|StaticCall>>>
|
||||
*/
|
||||
private $callsByTypeAndMethod = [];
|
||||
|
||||
/**
|
||||
* E.g. [$this, 'someLocalMethod']
|
||||
* @var ArrayCallable[][][]
|
||||
* @var array<string, array<string, ArrayCallable[]>>
|
||||
*/
|
||||
private $arrayCallablesByTypeAndMethod = [];
|
||||
|
||||
@ -121,13 +123,19 @@ final class NodeRepository
|
||||
*/
|
||||
private $names = [];
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ArrayCallableMethodReferenceAnalyzer $arrayCallableMethodReferenceAnalyzer,
|
||||
ParsedPropertyFetchNodeCollector $parsedPropertyFetchNodeCollector,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ParsedClassConstFetchNodeCollector $parsedClassConstFetchNodeCollector,
|
||||
ParsedNodeCollector $parsedNodeCollector,
|
||||
TypeUnwrapper $typeUnwrapper
|
||||
TypeUnwrapper $typeUnwrapper,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->arrayCallableMethodReferenceAnalyzer = $arrayCallableMethodReferenceAnalyzer;
|
||||
@ -135,6 +143,7 @@ final class NodeRepository
|
||||
$this->parsedClassConstFetchNodeCollector = $parsedClassConstFetchNodeCollector;
|
||||
$this->parsedNodeCollector = $parsedNodeCollector;
|
||||
$this->typeUnwrapper = $typeUnwrapper;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,7 +209,7 @@ final class NodeRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MethodCall[][]|StaticCall[][]
|
||||
* @return array<string, MethodCall[]|StaticCall[]>
|
||||
*/
|
||||
public function findMethodCallsOnClass(string $className): array
|
||||
{
|
||||
@ -226,7 +235,12 @@ final class NodeRepository
|
||||
$classMethods = [];
|
||||
|
||||
foreach ($this->classMethodsByType as $className => $classMethodByMethodName) {
|
||||
if (! is_a($className, $desiredType, true)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! $classReflection->isSubclassOf($desiredType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -271,15 +285,14 @@ final class NodeRepository
|
||||
return $this->classMethodsByType[$className][$methodName];
|
||||
}
|
||||
|
||||
$parentClass = $className;
|
||||
|
||||
if (! class_exists($parentClass)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while ($parentClass = get_parent_class($parentClass)) {
|
||||
if (isset($this->classMethodsByType[$parentClass][$methodName])) {
|
||||
return $this->classMethodsByType[$parentClass][$methodName];
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if (isset($this->classMethodsByType[$parentClassReflection->getName()][$methodName])) {
|
||||
return $this->classMethodsByType[$parentClassReflection->getName()][$methodName];
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,36 +379,46 @@ final class NodeRepository
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
public function findDirectClassConstantFetches(string $desiredClassName, string $desiredConstantName): array
|
||||
public function findDirectClassConstantFetches(ClassReflection $classReflection, string $desiredConstantName): array
|
||||
{
|
||||
$classConstantFetchByClassAndName = $this->parsedClassConstFetchNodeCollector->getClassConstantFetchByClassAndName();
|
||||
return $classConstantFetchByClassAndName[$desiredClassName][$desiredConstantName] ?? [];
|
||||
$classTypes = $classConstantFetchByClassAndName[$classReflection->getName()][$desiredConstantName] ?? [];
|
||||
|
||||
return $this->resolveClassReflectionsFromClassTypes($classTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
public function findIndirectClassConstantFetches(string $desiredClassName, string $desiredConstantName): array
|
||||
{
|
||||
public function findIndirectClassConstantFetches(
|
||||
ClassReflection $classReflection,
|
||||
string $desiredConstantName
|
||||
): array {
|
||||
$classConstantFetchByClassAndName = $this->parsedClassConstFetchNodeCollector->getClassConstantFetchByClassAndName();
|
||||
|
||||
foreach ($classConstantFetchByClassAndName as $className => $classesByConstantName) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$currentClassReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! isset($classesByConstantName[$desiredConstantName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $classReflection->isSubclassOf($currentClassReflection->getName()) &&
|
||||
! $currentClassReflection->isSubclassOf($classReflection->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// include child usages and parent usages
|
||||
if (! is_a($className, $desiredClassName, true) && ! is_a($desiredClassName, $className, true)) {
|
||||
if ($currentClassReflection->getName() === $classReflection->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($desiredClassName === $className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $classesByConstantName[$desiredConstantName];
|
||||
return $this->resolveClassReflectionsFromClassTypes($classesByConstantName[$desiredConstantName]);
|
||||
}
|
||||
|
||||
return [];
|
||||
@ -638,6 +661,17 @@ final class NodeRepository
|
||||
return $this->parsedNodeCollector->getClassConstFetches();
|
||||
}
|
||||
|
||||
public function resolveCallerClassName(MethodCall $methodCall): ?string
|
||||
{
|
||||
$callerType = $this->nodeTypeResolver->getStaticType($methodCall->var);
|
||||
$callerObjectType = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($callerType);
|
||||
if (! $callerObjectType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $callerObjectType->getClassName();
|
||||
}
|
||||
|
||||
private function collectArray(Array_ $array): void
|
||||
{
|
||||
$arrayCallable = $this->arrayCallableMethodReferenceAnalyzer->match($array);
|
||||
@ -645,7 +679,16 @@ final class NodeRepository
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $arrayCallable->isExistingMethod()) {
|
||||
if (! $this->reflectionProvider->hasClass($arrayCallable->getClass())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($arrayCallable->getClass());
|
||||
if (! $classReflection->isClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $classReflection->hasMethod($arrayCallable->getMethod())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -655,6 +698,7 @@ final class NodeRepository
|
||||
private function addMethod(ClassMethod $classMethod): void
|
||||
{
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
// anonymous
|
||||
if ($className === null) {
|
||||
return;
|
||||
@ -704,10 +748,20 @@ final class NodeRepository
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! is_a($currentClassName, $desiredClass, true)) {
|
||||
if (! $this->reflectionProvider->hasClass($desiredClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($currentClassName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$desiredClassReflection = $this->reflectionProvider->getClass($desiredClass);
|
||||
$currentClassReflection = $this->reflectionProvider->getClass($currentClassName);
|
||||
|
||||
if (! $currentClassReflection->isSubclassOf($desiredClassReflection->getName())) {
|
||||
return false;
|
||||
}
|
||||
return $currentClassName !== $desiredClass;
|
||||
}
|
||||
|
||||
@ -731,23 +785,11 @@ final class NodeRepository
|
||||
return $implementerInterfaces;
|
||||
}
|
||||
|
||||
private function resolveCallerClassName(MethodCall $methodCall): ?string
|
||||
{
|
||||
$callerType = $this->nodeTypeResolver->getStaticType($methodCall->var);
|
||||
$callerObjectType = $this->typeUnwrapper->unwrapFirstObjectTypeFromUnionType($callerType);
|
||||
if (! $callerObjectType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $callerObjectType->getClassName();
|
||||
}
|
||||
|
||||
private function resolveNodeClassTypes(Node $node): Type
|
||||
{
|
||||
if ($node instanceof MethodCall && $node->var instanceof Variable && $node->var->name === 'this') {
|
||||
/** @var string|null $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
if ($className) {
|
||||
return new ObjectType($className);
|
||||
}
|
||||
@ -781,4 +823,22 @@ final class NodeRepository
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string[] $classTypes
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
private function resolveClassReflectionsFromClassTypes(array $classTypes): array
|
||||
{
|
||||
$classReflections = [];
|
||||
foreach ($classTypes as $classType) {
|
||||
if (! $this->reflectionProvider->hasClass($classType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classReflections[] = $this->reflectionProvider->getClass($classType);
|
||||
}
|
||||
|
||||
return $classReflections;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Rector\NodeCollector\NodeCollector;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
@ -13,12 +14,11 @@ use PHPStan\Type\UnionType;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use ReflectionClass;
|
||||
|
||||
final class ParsedClassConstFetchNodeCollector
|
||||
{
|
||||
/**
|
||||
* @var string[][][]
|
||||
* @var array<string, array<string, class-string[]>>
|
||||
*/
|
||||
private $classConstantFetchByClassAndName = [];
|
||||
|
||||
@ -32,9 +32,15 @@ final class ParsedClassConstFetchNodeCollector
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver)
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,6 +76,7 @@ final class ParsedClassConstFetchNodeCollector
|
||||
}
|
||||
|
||||
// current class
|
||||
/** @var string $classOfUse */
|
||||
$classOfUse = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
$this->classConstantFetchByClassAndName[$className][$constantName][] = $classOfUse;
|
||||
@ -80,7 +87,7 @@ final class ParsedClassConstFetchNodeCollector
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[][][]
|
||||
* @return array<string, array<string, class-string[]>>
|
||||
*/
|
||||
public function getClassConstantFetchByClassAndName(): array
|
||||
{
|
||||
@ -137,14 +144,17 @@ final class ParsedClassConstFetchNodeCollector
|
||||
*/
|
||||
private function getConstantsDefinedInClass(string $className): array
|
||||
{
|
||||
$reflectionClass = new ReflectionClass($className);
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
$reflectionClass = $classReflection->getNativeReflection();
|
||||
|
||||
$constants = $reflectionClass->getConstants();
|
||||
|
||||
$currentClassConstants = array_keys($constants);
|
||||
$parentClassReflection = $reflectionClass->getParentClass();
|
||||
|
||||
if (! $parentClassReflection) {
|
||||
if ($classReflection->getParentClass() !== false) {
|
||||
return $currentClassConstants;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\Native\NativeMethodReflection;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
@ -21,7 +22,6 @@ use Rector\Core\ValueObject\MethodName;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use ReflectionMethod;
|
||||
|
||||
final class MethodReflectionProvider
|
||||
{
|
||||
@ -70,7 +70,7 @@ final class MethodReflectionProvider
|
||||
return $parameterTypes;
|
||||
}
|
||||
|
||||
public function provideByMethodCall(MethodCall $methodCall): ?ReflectionMethod
|
||||
public function provideByMethodCall(MethodCall $methodCall): ?MethodReflection
|
||||
{
|
||||
$className = $methodCall->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if (! is_string($className)) {
|
||||
@ -82,21 +82,16 @@ final class MethodReflectionProvider
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! method_exists($className, $methodName)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ReflectionMethod($className, $methodName);
|
||||
}
|
||||
|
||||
public function provideByClassAndMethodName(string $class, string $method, Scope $scope): ?MethodReflection
|
||||
{
|
||||
$classReflection = $this->reflectionProvider->getClass($class);
|
||||
if (! $classReflection->hasMethod($method)) {
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! $classReflection->hasMethod($methodName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $classReflection->getMethod($method, $scope);
|
||||
return $classReflection->getNativeMethod($methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,7 +150,12 @@ final class MethodReflectionProvider
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->provideByClassAndMethodName($class, $method, $scope);
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $classReflection->getMethod($method, $scope);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,9 +163,8 @@ final class MethodReflectionProvider
|
||||
*/
|
||||
public function getParameterReflectionsFromMethodReflection(MethodReflection $methodReflection): array
|
||||
{
|
||||
$methodReflectionVariant = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
|
||||
|
||||
return $methodReflectionVariant->getParameters();
|
||||
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
|
||||
return $parametersAcceptor->getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,18 +173,27 @@ final class MethodReflectionProvider
|
||||
public function provideParameterNamesByNew(New_ $new): array
|
||||
{
|
||||
$objectType = $this->nodeTypeResolver->resolve($new->class);
|
||||
|
||||
$classes = TypeUtils::getDirectClassNames($objectType);
|
||||
|
||||
$parameterNames = [];
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if (! method_exists($class, MethodName::CONSTRUCT)) {
|
||||
if (! $this->reflectionProvider->hasClass($class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodReflection = new ReflectionMethod($class, MethodName::CONSTRUCT);
|
||||
$classReflection = $this->reflectionProvider->getClass($class);
|
||||
|
||||
if (! $classReflection->hasMethod(MethodName::CONSTRUCT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nativeClassReflection = $classReflection->getNativeReflection();
|
||||
$methodReflection = $nativeClassReflection->getMethod(MethodName::CONSTRUCT);
|
||||
|
||||
foreach ($methodReflection->getParameters() as $reflectionParameter) {
|
||||
$parameterNames[] = $reflectionParameter->name;
|
||||
$parameterNames[] = $reflectionParameter->getName();
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,7 +215,9 @@ final class MethodReflectionProvider
|
||||
}
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$methodReflection = $this->provideByClassAndMethodName($class, $methodName, $scope);
|
||||
$classReflection = $this->reflectionProvider->getClass($class);
|
||||
$methodReflection = $classReflection->getMethod($methodName, $scope);
|
||||
|
||||
if ($methodReflection instanceof MethodReflection) {
|
||||
return $methodReflection;
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ declare(strict_types=1);
|
||||
namespace Rector\NodeCollector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use ReflectionClass;
|
||||
|
||||
final class StaticAnalyzer
|
||||
{
|
||||
@ -16,9 +17,15 @@ final class StaticAnalyzer
|
||||
*/
|
||||
private $nodeRepository;
|
||||
|
||||
public function __construct(NodeRepository $nodeRepository)
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(NodeRepository $nodeRepository, ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function isStaticMethod(string $methodName, string $className): bool
|
||||
@ -30,29 +37,33 @@ final class StaticAnalyzer
|
||||
|
||||
// could be static in doc type magic
|
||||
// @see https://regex101.com/r/tlvfTB/1
|
||||
if (! ClassExistenceStaticHelper::doesClassLikeExist($className)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$reflectionClass = new ReflectionClass($className);
|
||||
if ($this->hasStaticAnnotation($methodName, $reflectionClass)) {
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if ($this->hasStaticAnnotation($methodName, $classReflection)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// probably magic method → we don't know
|
||||
if (! method_exists($className, $methodName)) {
|
||||
if (! $classReflection->hasMethod($methodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodReflection = $reflectionClass->getMethod($methodName);
|
||||
|
||||
$methodReflection = $classReflection->getNativeMethod($methodName);
|
||||
return $methodReflection->isStatic();
|
||||
}
|
||||
|
||||
private function hasStaticAnnotation(string $methodName, ReflectionClass $reflectionClass): bool
|
||||
private function hasStaticAnnotation(string $methodName, ClassReflection $classReflection): bool
|
||||
{
|
||||
$resolvedPhpDocBlock = $classReflection->getResolvedPhpDoc();
|
||||
if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) Strings::match(
|
||||
(string) $reflectionClass->getDocComment(),
|
||||
$resolvedPhpDocBlock->getPhpDocString(),
|
||||
'#@method\s*static\s*(.*?)\b' . $methodName . '\b#'
|
||||
);
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeCollector\ValueObject;
|
||||
|
||||
use ReflectionMethod;
|
||||
|
||||
final class ArrayCallable
|
||||
{
|
||||
/**
|
||||
@ -33,14 +31,4 @@ final class ArrayCallable
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function isExistingMethod(): bool
|
||||
{
|
||||
return method_exists($this->class, $this->method);
|
||||
}
|
||||
|
||||
public function getReflectionMethod(): ReflectionMethod
|
||||
{
|
||||
return new ReflectionMethod($this->class, $this->method);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\CodingStyle\Naming\ClassNaming;
|
||||
use Rector\Core\Contract\Rector\RectorInterface;
|
||||
@ -61,6 +62,11 @@ final class NodeNameResolver
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @param NodeNameResolverInterface[] $nodeNameResolvers
|
||||
*/
|
||||
@ -69,6 +75,7 @@ final class NodeNameResolver
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
CurrentFileInfoProvider $currentFileInfoProvider,
|
||||
ClassNaming $classNaming,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
array $nodeNameResolvers = []
|
||||
) {
|
||||
$this->regexPatternDetector = $regexPatternDetector;
|
||||
@ -76,6 +83,7 @@ final class NodeNameResolver
|
||||
$this->currentFileInfoProvider = $currentFileInfoProvider;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->classNaming = $classNaming;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -331,17 +339,17 @@ final class NodeNameResolver
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $desiredClassNames
|
||||
* @param ObjectType[] $desiredObjectTypes
|
||||
*/
|
||||
public function isInClassNames(Node $node, array $desiredClassNames): bool
|
||||
public function isInClassNames(Node $node, array $desiredObjectTypes): bool
|
||||
{
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($desiredClassNames as $desiredClassName) {
|
||||
if (is_a($className, $desiredClassName, true)) {
|
||||
foreach ($desiredObjectTypes as $desiredObjectType) {
|
||||
if ($this->isInClassNamed($classNode, $desiredObjectType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -356,7 +364,16 @@ final class NodeNameResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_a($className, $objectType->getClassName(), true);
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if ($classReflection->getName() === $objectType->getClassName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $classReflection->isSubclassOf($objectType->getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,11 +9,22 @@ use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\NodeNameResolver\Contract\NodeNameResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class FuncCallNameResolver implements NodeNameResolverInterface
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getNode(): string
|
||||
{
|
||||
return FuncCall::class;
|
||||
@ -39,7 +50,8 @@ final class FuncCallNameResolver implements NodeNameResolverInterface
|
||||
$namespaceName = $functionName->getAttribute(AttributeKey::NAMESPACED_NAME);
|
||||
if ($namespaceName instanceof FullyQualified) {
|
||||
$functionFqnName = $namespaceName->toString();
|
||||
if (function_exists($functionFqnName)) {
|
||||
|
||||
if ($this->reflectionProvider->hasFunction($namespaceName, null)) {
|
||||
return $functionFqnName;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ use Rector\Core\Php\TypeAnalyzer;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory;
|
||||
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator;
|
||||
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
|
||||
|
||||
@ -29,7 +31,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
->autoconfigure();
|
||||
|
||||
$services->load('Rector\NodeTypeResolver\\', __DIR__ . '/../src')
|
||||
->exclude([__DIR__ . '/../src/Contract']);
|
||||
->exclude([__DIR__ . '/../src/Contract', __DIR__ . '/../src/Reflection/BetterReflection']);
|
||||
|
||||
$services->set(IntermediateSourceLocator::class);
|
||||
|
||||
$services->set(TypeAnalyzer::class);
|
||||
$services->set(FilesFinder::class);
|
||||
@ -48,5 +52,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services->set(TypeNodeResolver::class)
|
||||
->factory([service(PHPStanServicesFactory::class), 'createTypeNodeResolver']);
|
||||
|
||||
$services->set(DynamicSourceLocatorProvider::class)
|
||||
->factory([service(PHPStanServicesFactory::class), 'createDynamicSourceLocatorProvider']);
|
||||
|
||||
$services->set(NodeConnectingVisitor::class);
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
parameters:
|
||||
# see https://github.com/rectorphp/rector/issues/3490#issue-634342324
|
||||
featureToggles:
|
||||
disableRuntimeReflectionProvider: false
|
||||
|
||||
services:
|
||||
- Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory
|
||||
- Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator
|
||||
- Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider
|
||||
|
||||
# basically decorates native PHPStan source locator with a dynamic source locator that is also available in Rector DI
|
||||
betterReflectionSourceLocator:
|
||||
class: PHPStan\BetterReflection\SourceLocator\Type\SourceLocator
|
||||
factory: ['@Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory', 'create']
|
||||
autowired: false
|
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
final class ClassExistenceStaticHelper
|
||||
{
|
||||
public static function doesClassLikeExist(string $classLike): bool
|
||||
{
|
||||
if (class_exists($classLike)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (interface_exists($classLike)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return trait_exists($classLike);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Contract;
|
||||
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
|
||||
|
||||
interface SourceLocatorProviderInterface
|
||||
{
|
||||
public function provide(): SourceLocator;
|
||||
}
|
@ -15,6 +15,7 @@ use PHPStan\File\FileHelper;
|
||||
use PHPStan\PhpDoc\TypeNodeResolver;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
|
||||
/**
|
||||
@ -34,6 +35,8 @@ final class PHPStanServicesFactory
|
||||
|
||||
$additionalConfigFiles = [];
|
||||
$additionalConfigFiles[] = $parameterProvider->provideStringParameter(Option::PHPSTAN_FOR_RECTOR_PATH);
|
||||
$additionalConfigFiles[] = __DIR__ . '/../../config/phpstan/static-reflection.neon';
|
||||
$additionalConfigFiles[] = __DIR__ . '/../../config/phpstan/better-infer.neon';
|
||||
|
||||
$existingAdditionalConfigFiles = array_filter($additionalConfigFiles, 'file_exists');
|
||||
|
||||
@ -103,4 +106,12 @@ final class PHPStanServicesFactory
|
||||
{
|
||||
return $this->container->getByType(TypeNodeResolver::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function createDynamicSourceLocatorProvider(): DynamicSourceLocatorProvider
|
||||
{
|
||||
return $this->container->getByType(DynamicSourceLocatorProvider::class);
|
||||
}
|
||||
}
|
||||
|
@ -4,23 +4,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeTypeCorrector;
|
||||
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use PHPStan\Type\Generic\GenericClassStringType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeTraverser;
|
||||
use Symplify\PackageBuilder\Reflection\ClassLikeExistenceChecker;
|
||||
|
||||
final class GenericClassStringTypeCorrector
|
||||
{
|
||||
/**
|
||||
* @var ClassLikeExistenceChecker
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $classLikeExistenceChecker;
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ClassLikeExistenceChecker $classLikeExistenceChecker)
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->classLikeExistenceChecker = $classLikeExistenceChecker;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function correct(Type $mainType): Type
|
||||
@ -31,7 +31,7 @@ final class GenericClassStringTypeCorrector
|
||||
return $traverse($type);
|
||||
}
|
||||
|
||||
if (! $this->classLikeExistenceChecker->doesClassLikeExist($type->getValue())) {
|
||||
if (! $this->reflectionProvider->hasClass($type->getValue())) {
|
||||
return $traverse($type);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@ use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
|
||||
|
||||
@ -42,7 +41,7 @@ final class ParentClassLikeTypeCorrector
|
||||
public function correct(Type $type): Type
|
||||
{
|
||||
if ($type instanceof TypeWithClassName) {
|
||||
if (! ClassExistenceStaticHelper::doesClassLikeExist($type->getClassName())) {
|
||||
if (! $this->reflectionProvider->hasClass($type->getClassName())) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
@ -54,7 +53,7 @@ final class ParentClassLikeTypeCorrector
|
||||
|
||||
$allTypes = [];
|
||||
foreach ($classNames as $className) {
|
||||
if (! ClassExistenceStaticHelper::doesClassLikeExist($className)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
||||
namespace Rector\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
@ -34,7 +33,6 @@ use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
||||
use Rector\Core\Util\StaticInstanceOf;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
|
||||
@ -144,7 +142,6 @@ final class NodeTypeResolver
|
||||
|
||||
// this should also work with ObjectType and UnionType with ObjectType
|
||||
// use PHPStan types here
|
||||
|
||||
if ($resolvedType->equals($requiredObjectType)) {
|
||||
return true;
|
||||
}
|
||||
@ -155,6 +152,15 @@ final class NodeTypeResolver
|
||||
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) {
|
||||
return $type;
|
||||
}
|
||||
@ -187,24 +193,24 @@ final class NodeTypeResolver
|
||||
*/
|
||||
public function getStaticType(Node $node): Type
|
||||
{
|
||||
if ($this->isArrayExpr($node)) {
|
||||
/** @var Expr $node */
|
||||
return $this->resolveArrayType($node);
|
||||
}
|
||||
|
||||
if ($node instanceof Arg) {
|
||||
throw new ShouldNotHappenException('Arg cannot have type, use $arg->value instead');
|
||||
}
|
||||
|
||||
if (StaticInstanceOf::isOneOf($node, [Param::class, Scalar::class])) {
|
||||
if ($node instanceof Param) {
|
||||
return $this->resolve($node);
|
||||
}
|
||||
|
||||
$nodeScope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $node instanceof Expr) {
|
||||
return new MixedType();
|
||||
}
|
||||
if (! $nodeScope instanceof Scope) {
|
||||
|
||||
if ($this->arrayTypeAnalyzer->isArrayType($node)) {
|
||||
return $this->resolveArrayType($node);
|
||||
}
|
||||
|
||||
if ($node instanceof Scalar) {
|
||||
return $this->resolve($node);
|
||||
}
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
@ -215,7 +221,7 @@ final class NodeTypeResolver
|
||||
}
|
||||
}
|
||||
|
||||
$staticType = $nodeScope->getType($node);
|
||||
$staticType = $scope->getType($node);
|
||||
if (! $staticType instanceof ObjectType) {
|
||||
return $staticType;
|
||||
}
|
||||
@ -228,9 +234,13 @@ final class NodeTypeResolver
|
||||
if ($this->isStaticType($node, IntegerType::class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isStaticType($node, FloatType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string<Type> $staticTypeClass
|
||||
*/
|
||||
public function isStaticType(Node $node, string $staticTypeClass): bool
|
||||
{
|
||||
if (! is_a($staticTypeClass, Type::class, true)) {
|
||||
@ -389,7 +399,9 @@ final class NodeTypeResolver
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
if ($unionedType->equals($requiredObjectType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -432,14 +444,6 @@ final class NodeTypeResolver
|
||||
return $this->resolveFirstType($node->var);
|
||||
}
|
||||
|
||||
private function isArrayExpr(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Expr) {
|
||||
return false;
|
||||
}
|
||||
return $this->arrayTypeAnalyzer->isArrayType($node);
|
||||
}
|
||||
|
||||
private function resolveArrayType(Expr $expr): Type
|
||||
{
|
||||
/** @var Scope|null $scope */
|
||||
|
@ -27,7 +27,7 @@ final class ArrayDimFetchTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ final class CastTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ final class ClassAndInterfaceTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ final class ClassConstFetchTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ final class ClassMethodOrClassConstTypeResolver implements NodeTypeResolverInter
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
@ -20,7 +21,17 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
final class NameTypeResolver implements NodeTypeResolverInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
@ -45,22 +56,31 @@ final class NameTypeResolver implements NodeTypeResolverInterface
|
||||
*/
|
||||
private function resolveParent(Name $name): Type
|
||||
{
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $name->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
|
||||
// missing parent class, probably unused parent:: call
|
||||
if ($parentClassName === null) {
|
||||
$className = $name->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$type = new ObjectType($parentClassName);
|
||||
|
||||
$parentParentClass = get_parent_class($parentClassName);
|
||||
if ($parentParentClass) {
|
||||
$type = new UnionType([$type, new ObjectType($parentParentClass)]);
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return $type;
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
|
||||
$parentClassObjectTypes = [];
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
$parentClassObjectTypes[] = new ObjectType($parentClassReflection->getName());
|
||||
}
|
||||
|
||||
if ($parentClassObjectTypes === []) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
if (count($parentClassObjectTypes) === 1) {
|
||||
return $parentClassObjectTypes[0];
|
||||
}
|
||||
|
||||
return new UnionType($parentClassObjectTypes);
|
||||
}
|
||||
|
||||
private function resolveFullyQualifiedName(Name $name): string
|
||||
|
@ -75,7 +75,7 @@ final class ParamTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -5,32 +5,19 @@ declare(strict_types=1);
|
||||
namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Property;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Nop;
|
||||
use PhpParser\Node\Stmt\PropertyProperty;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\Node\VarLikeIdentifier;
|
||||
use PhpParser\Parser;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
/**
|
||||
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\PropertyFetchTypeResolver\PropertyFetchTypeResolverTest
|
||||
@ -47,52 +34,24 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var TraitNodeScopeCollector
|
||||
*/
|
||||
private $traitNodeScopeCollector;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var NodeRepository
|
||||
*/
|
||||
private $nodeRepository;
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
TraitNodeScopeCollector $traitNodeScopeCollector,
|
||||
SmartFileSystem $smartFileSystem,
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
Parser $parser,
|
||||
NodeRepository $nodeRepository
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->traitNodeScopeCollector = $traitNodeScopeCollector;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->smartFileSystem = $smartFileSystem;
|
||||
$this->parser = $parser;
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,7 +63,7 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
@ -152,79 +111,29 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
|
||||
|
||||
private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): Type
|
||||
{
|
||||
$varObjectType = $this->nodeTypeResolver->resolve($propertyFetch->var);
|
||||
if (! $varObjectType instanceof TypeWithClassName) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$class = $this->nodeRepository->findClass($varObjectType->getClassName());
|
||||
if ($class !== null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
// 3rd party code
|
||||
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
|
||||
if ($propertyName === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
if (! property_exists($varObjectType->getClassName(), $propertyName)) {
|
||||
$varType = $this->nodeTypeResolver->resolve($propertyFetch->var);
|
||||
if (! $varType instanceof ObjectType) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$phpDocInfo = $propertyFetch->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
if (! $phpDocInfo instanceof PhpDocInfo && $varObjectType instanceof ObjectType) {
|
||||
return $this->getPropertyPropertyResolution($varObjectType, $propertyName);
|
||||
}
|
||||
|
||||
return $this->getTypeFromPhpDocInfo($phpDocInfo);
|
||||
}
|
||||
|
||||
private function getPropertyPropertyResolution(ObjectType $varObjectType, string $propertyName): Type
|
||||
{
|
||||
$classReflection = $varObjectType->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return $varObjectType;
|
||||
}
|
||||
|
||||
if ($classReflection->isBuiltIn()) {
|
||||
return $varObjectType;
|
||||
}
|
||||
|
||||
$nodes = $this->parser->parse($this->smartFileSystem->readFile((string) $classReflection->getFileName()));
|
||||
$propertyProperty = $this->betterNodeFinder->findFirst($nodes, function (Node $node) use (
|
||||
$propertyName
|
||||
): bool {
|
||||
if (! $node instanceof PropertyProperty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $node->name instanceof VarLikeIdentifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $node->name->toString() === $propertyName;
|
||||
});
|
||||
|
||||
if ($propertyProperty instanceof PropertyProperty) {
|
||||
return $this->nodeTypeResolver->resolve($propertyProperty);
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
private function getTypeFromPhpDocInfo(PhpDocInfo $phpDocInfo): Type
|
||||
{
|
||||
$tagValueNode = $phpDocInfo->getVarTagValueNode();
|
||||
if (! $tagValueNode instanceof VarTagValueNode) {
|
||||
if (! $this->reflectionProvider->hasClass($varType->getClassName())) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$typeNode = $tagValueNode->type;
|
||||
if (! $typeNode instanceof TypeNode) {
|
||||
$classReflection = $this->reflectionProvider->getClass($varType->getClassName());
|
||||
if (! $classReflection->hasProperty($propertyName)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, new Nop());
|
||||
$propertyFetchScope = $propertyFetch->getAttribute(AttributeKey::SCOPE);
|
||||
$propertyReflection = $classReflection->getProperty($propertyName, $propertyFetchScope);
|
||||
|
||||
return $propertyReflection->getReadableType();
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ final class PropertyTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -7,8 +7,10 @@ namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
@ -26,9 +28,15 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver)
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,7 +48,7 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
@ -60,15 +68,26 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
||||
return $classType;
|
||||
}
|
||||
|
||||
$classNames = TypeUtils::getDirectClassNames($classType);
|
||||
if (! $classType instanceof ObjectType) {
|
||||
return $classType;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($classType->getClassName())) {
|
||||
return $classType;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($classType->getClassName());
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return $classType;
|
||||
}
|
||||
|
||||
foreach ($classNames as $className) {
|
||||
if (! method_exists($className, $methodName)) {
|
||||
/** @var ClassReflection[] $currentAndParentClassReflections */
|
||||
$currentAndParentClassReflections = array_merge([$classReflection], $classReflection->getParents());
|
||||
|
||||
foreach ($currentAndParentClassReflections as $currentAndParentClassReflection) {
|
||||
if (! $currentAndParentClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,12 @@ namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @see \Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\TraitTypeResolver\TraitTypeResolverTest
|
||||
@ -19,7 +19,17 @@ use ReflectionClass;
|
||||
final class TraitTypeResolver implements NodeTypeResolverInterface
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
@ -31,12 +41,17 @@ final class TraitTypeResolver implements NodeTypeResolverInterface
|
||||
*/
|
||||
public function resolve(Node $traitNode): Type
|
||||
{
|
||||
$reflectionClass = new ReflectionClass((string) $traitNode->namespacedName);
|
||||
$traitName = (string) $traitNode->namespacedName;
|
||||
if (! $this->reflectionProvider->hasClass($traitName)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($traitName);
|
||||
|
||||
$types = [];
|
||||
$types[] = new ObjectType($reflectionClass->getName());
|
||||
$types[] = new ObjectType($traitName);
|
||||
|
||||
foreach ($reflectionClass->getTraits() as $usedTraitReflection) {
|
||||
foreach ($classReflection->getTraits() as $usedTraitReflection) {
|
||||
$types[] = new ObjectType($usedTraitReflection->getName());
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ final class VariableTypeResolver implements NodeTypeResolverInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PHPStan;
|
||||
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\ConstantType;
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
@ -54,11 +53,7 @@ final class TypeHasher
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantType) {
|
||||
if (method_exists($type, 'getValue')) {
|
||||
return get_class($type) . $type->getValue();
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
return get_class($type) . $type->getValue();
|
||||
}
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Reflection\BetterReflection;
|
||||
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
|
||||
use PHPStan\Reflection\BetterReflection\BetterReflectionSourceLocatorFactory;
|
||||
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator;
|
||||
|
||||
final class RectorBetterReflectionSourceLocatorFactory
|
||||
{
|
||||
/**
|
||||
* @var BetterReflectionSourceLocatorFactory
|
||||
*/
|
||||
private $betterReflectionSourceLocatorFactory;
|
||||
|
||||
/**
|
||||
* @var IntermediateSourceLocator
|
||||
*/
|
||||
private $intermediateSourceLocator;
|
||||
|
||||
public function __construct(
|
||||
BetterReflectionSourceLocatorFactory $betterReflectionSourceLocatorFactory,
|
||||
IntermediateSourceLocator $intermediateSourceLocator
|
||||
) {
|
||||
$this->betterReflectionSourceLocatorFactory = $betterReflectionSourceLocatorFactory;
|
||||
$this->intermediateSourceLocator = $intermediateSourceLocator;
|
||||
}
|
||||
|
||||
public function create(): SourceLocator
|
||||
{
|
||||
$phpStanSourceLocator = $this->betterReflectionSourceLocatorFactory->create();
|
||||
return new AggregateSourceLocator([$this->intermediateSourceLocator, $phpStanSourceLocator]);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator;
|
||||
|
||||
use PHPStan\BetterReflection\Identifier\Identifier;
|
||||
use PHPStan\BetterReflection\Identifier\IdentifierType;
|
||||
use PHPStan\BetterReflection\Reflection\Reflection;
|
||||
use PHPStan\BetterReflection\Reflector\Reflector;
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
|
||||
use Rector\NodeTypeResolver\Contract\SourceLocatorProviderInterface;
|
||||
|
||||
final class IntermediateSourceLocator implements SourceLocator
|
||||
{
|
||||
/**
|
||||
* @var SourceLocatorProviderInterface[]
|
||||
*/
|
||||
private $sourceLocatorProviders = [];
|
||||
|
||||
/**
|
||||
* @param SourceLocatorProviderInterface[] $sourceLocatorProviders
|
||||
*/
|
||||
public function __construct(array $sourceLocatorProviders)
|
||||
{
|
||||
$this->sourceLocatorProviders = $sourceLocatorProviders;
|
||||
}
|
||||
|
||||
public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection
|
||||
{
|
||||
foreach ($this->sourceLocatorProviders as $sourceLocatorProvider) {
|
||||
$sourceLocator = $sourceLocatorProvider->provide();
|
||||
|
||||
$reflection = $sourceLocator->locateIdentifier($reflector, $identifier);
|
||||
if ($reflection instanceof Reflection) {
|
||||
return $reflection;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all identifiers of a type
|
||||
* @return Reflection[]
|
||||
*/
|
||||
public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array
|
||||
{
|
||||
foreach ($this->sourceLocatorProviders as $sourceLocatorProvider) {
|
||||
$sourceLocator = $sourceLocatorProvider->provide();
|
||||
|
||||
$reflections = $sourceLocator->locateIdentifiersByType($reflector, $identifierType);
|
||||
if ($reflections !== []) {
|
||||
return $reflections;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider;
|
||||
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocator;
|
||||
use Rector\NodeTypeResolver\Contract\SourceLocatorProviderInterface;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class DynamicSourceLocatorProvider implements SourceLocatorProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var SmartFileInfo[]
|
||||
*/
|
||||
private $fileInfos = [];
|
||||
|
||||
/**
|
||||
* @var FileNodesFetcher
|
||||
*/
|
||||
private $fileNodesFetcher;
|
||||
|
||||
public function __construct(FileNodesFetcher $fileNodesFetcher)
|
||||
{
|
||||
$this->fileNodesFetcher = $fileNodesFetcher;
|
||||
}
|
||||
|
||||
public function setFileInfo(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->fileInfos = [$fileInfo];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $fileInfos
|
||||
*/
|
||||
public function addFileInfos(array $fileInfos): void
|
||||
{
|
||||
$this->fileInfos = array_merge($this->fileInfos, $fileInfos);
|
||||
}
|
||||
|
||||
public function provide(): SourceLocator
|
||||
{
|
||||
$sourceLocators = [];
|
||||
foreach ($this->fileInfos as $fileInfo) {
|
||||
$sourceLocators[] = new OptimizedSingleFileSourceLocator($this->fileNodesFetcher, $fileInfo->getRealPath());
|
||||
}
|
||||
|
||||
return new AggregateSourceLocator($sourceLocators);
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
|
||||
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
||||
@ -35,14 +36,21 @@ final class AnnotationToAttributeConverter
|
||||
*/
|
||||
private $phpDocTagRemover;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
PhpAttributeGroupFactory $phpAttributeGroupFactory,
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
PhpDocTagRemover $phpDocTagRemover
|
||||
PhpDocTagRemover $phpDocTagRemover,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->phpAttributeGroupFactory = $phpAttributeGroupFactory;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->phpDocTagRemover = $phpDocTagRemover;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +104,7 @@ final class AnnotationToAttributeConverter
|
||||
return array_filter(
|
||||
$phpAttributableTagNodes,
|
||||
function (PhpAttributableTagNodeInterface $phpAttributableTagNode): bool {
|
||||
return class_exists($phpAttributableTagNode->getAttributeClassName());
|
||||
return $this->reflectionProvider->hasClass($phpAttributableTagNode->getAttributeClassName());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -4,16 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanStaticTypeMapper\TypeAnalyzer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use PHPStan\Type\Generic\GenericClassStringType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
|
||||
|
||||
@ -29,10 +30,7 @@ final class UnionTypeCommonTypeNarrower
|
||||
BinaryOp::class => [BinaryOp::class, Expr::class],
|
||||
Expr::class => [Node::class, Expr::class],
|
||||
Stmt::class => [Node::class, Stmt::class],
|
||||
'PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode' => [
|
||||
'PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode',
|
||||
'PHPStan\PhpDocParser\Ast\Node',
|
||||
],
|
||||
PhpDocTagValueNode::class => [PhpDocTagValueNode::class, \PHPStan\PhpDocParser\Ast\Node::class],
|
||||
];
|
||||
|
||||
/**
|
||||
@ -40,9 +38,17 @@ final class UnionTypeCommonTypeNarrower
|
||||
*/
|
||||
private $genericClassStringTypeCorrector;
|
||||
|
||||
public function __construct(GenericClassStringTypeCorrector $genericClassStringTypeCorrector)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
GenericClassStringTypeCorrector $genericClassStringTypeCorrector,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->genericClassStringTypeCorrector = $genericClassStringTypeCorrector;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function narrowToSharedObjectType(UnionType $unionType): ?ObjectType
|
||||
@ -79,9 +85,15 @@ final class UnionTypeCommonTypeNarrower
|
||||
return $unionType;
|
||||
}
|
||||
|
||||
if ($unionedType->getGenericType() instanceof TypeWithClassName) {
|
||||
$availableTypes[] = $this->resolveClassParentClassesAndInterfaces($unionedType->getGenericType());
|
||||
$genericClassStrings = [];
|
||||
if ($unionedType->getGenericType() instanceof ObjectType) {
|
||||
$parentClassReflections = $this->resolveClassParentClassesAndInterfaces($unionedType->getGenericType());
|
||||
foreach ($parentClassReflections as $classReflection) {
|
||||
$genericClassStrings[] = $classReflection->getName();
|
||||
}
|
||||
}
|
||||
|
||||
$availableTypes[] = $genericClassStrings;
|
||||
}
|
||||
|
||||
$genericClassStringType = $this->createGenericClassStringType($availableTypes);
|
||||
@ -100,57 +112,43 @@ final class UnionTypeCommonTypeNarrower
|
||||
$availableTypes = [];
|
||||
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if (! $unionedType instanceof TypeWithClassName) {
|
||||
if (! $unionedType instanceof ObjectType) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$availableTypes[] = $this->resolveClassParentClassesAndInterfaces($unionedType);
|
||||
$typeClassReflections = $this->resolveClassParentClassesAndInterfaces($unionedType);
|
||||
$typeClassNames = [];
|
||||
foreach ($typeClassReflections as $classReflection) {
|
||||
$typeClassNames[] = $classReflection->getName();
|
||||
}
|
||||
|
||||
$availableTypes[] = $typeClassNames;
|
||||
}
|
||||
|
||||
return $this->narrowAvailableTypes($availableTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
private function resolveClassParentClassesAndInterfaces(TypeWithClassName $typeWithClassName): array
|
||||
private function resolveClassParentClassesAndInterfaces(ObjectType $objectType): array
|
||||
{
|
||||
$parentClasses = class_parents($typeWithClassName->getClassName());
|
||||
if ($parentClasses === false) {
|
||||
$parentClasses = [];
|
||||
if (! $this->reflectionProvider->hasClass($objectType->getClassName())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$implementedInterfaces = class_implements($typeWithClassName->getClassName());
|
||||
if ($implementedInterfaces === false) {
|
||||
$implementedInterfaces = [];
|
||||
}
|
||||
|
||||
$implementedInterfaces = $this->filterOutNativeInterfaces($implementedInterfaces);
|
||||
$classReflection = $this->reflectionProvider->getClass($objectType->getClassName());
|
||||
|
||||
// put earliest interfaces first
|
||||
$implementedInterfaces = array_reverse($implementedInterfaces);
|
||||
$implementedInterfaceClassReflections = array_reverse($classReflection->getInterfaces());
|
||||
|
||||
$classParentClassesAndInterfaces = array_merge($implementedInterfaces, $parentClasses);
|
||||
/** @var ClassReflection[] $parentClassAndInterfaceReflections */
|
||||
$parentClassAndInterfaceReflections = array_merge(
|
||||
$implementedInterfaceClassReflections,
|
||||
$classReflection->getParents()
|
||||
);
|
||||
|
||||
return array_unique($classParentClassesAndInterfaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string[] $interfaces
|
||||
* @return class-string[]
|
||||
*/
|
||||
private function filterOutNativeInterfaces(array $interfaces): array
|
||||
{
|
||||
foreach ($interfaces as $key => $implementedInterface) {
|
||||
// remove native interfaces
|
||||
if (Strings::contains($implementedInterface, '\\')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset($interfaces[$key]);
|
||||
}
|
||||
|
||||
return $interfaces;
|
||||
return $this->filterOutNativeClassReflections($parentClassAndInterfaceReflections);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,4 +183,15 @@ final class UnionTypeCommonTypeNarrower
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassReflection[] $classReflections
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
private function filterOutNativeClassReflections(array $classReflections): array
|
||||
{
|
||||
return array_filter($classReflections, function (ClassReflection $classReflection): bool {
|
||||
return ! $classReflection->isBuiltin();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\Constant\ConstantIntegerType;
|
||||
@ -27,7 +28,6 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeCommonTypeNarrower;
|
||||
use Rector\TypeDeclaration\TypeNormalizer;
|
||||
use Symplify\PackageBuilder\Reflection\ClassLikeExistenceChecker;
|
||||
|
||||
/**
|
||||
* @see \Rector\PHPStanStaticTypeMapper\Tests\TypeMapper\ArrayTypeMapperTest
|
||||
@ -55,23 +55,24 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
||||
private $unionTypeCommonTypeNarrower;
|
||||
|
||||
/**
|
||||
* @var ClassLikeExistenceChecker
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $classLikeExistenceChecker;
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* To avoid circular dependency
|
||||
* @required
|
||||
*/
|
||||
public function autowireArrayTypeMapper(
|
||||
PHPStanStaticTypeMapper $phpStanStaticTypeMapper,
|
||||
TypeNormalizer $typeNormalizer,
|
||||
UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower,
|
||||
ClassLikeExistenceChecker $classLikeExistenceChecker
|
||||
ReflectionProvider $reflectionProvider
|
||||
): void {
|
||||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
$this->typeNormalizer = $typeNormalizer;
|
||||
$this->unionTypeCommonTypeNarrower = $unionTypeCommonTypeNarrower;
|
||||
$this->classLikeExistenceChecker = $classLikeExistenceChecker;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
@ -254,9 +255,7 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
||||
GenericClassStringType $genericClassStringType
|
||||
): AttributeAwareNodeInterface {
|
||||
$genericType = $genericClassStringType->getGenericType();
|
||||
if ($genericType instanceof ObjectType && ! $this->classLikeExistenceChecker->doesClassLikeExist(
|
||||
$genericType->getClassName()
|
||||
)) {
|
||||
if ($genericType instanceof ObjectType && ! $this->reflectionProvider->hasClass($genericType->getClassName())) {
|
||||
return new AttributeAwareIdentifierTypeNode($genericType->getClassName());
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,13 @@ use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareGenericTypeNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareIdentifierTypeNode;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
@ -32,6 +32,16 @@ final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMa
|
||||
*/
|
||||
private $phpStanStaticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ObjectType::class;
|
||||
@ -120,7 +130,7 @@ final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMa
|
||||
return '\\' . $type->getClassName();
|
||||
}
|
||||
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($type->getClassName())) {
|
||||
if ($this->reflectionProvider->hasClass($type->getClassName())) {
|
||||
// FQN by default
|
||||
return '\\' . $type->describe(VerbosityLevel::typeOnly());
|
||||
}
|
||||
|
@ -5,12 +5,11 @@ declare(strict_types=1);
|
||||
namespace Rector\PostRector\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Nette\NetteInjectTagNode;
|
||||
use Rector\Core\ValueObject\MethodName;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
final class NetteInjectDetector
|
||||
{
|
||||
@ -24,10 +23,19 @@ final class NetteInjectDetector
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, PhpDocInfoFactory $phpDocInfoFactory)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function isNetteInjectPreferred(Class_ $class): bool
|
||||
@ -62,11 +70,16 @@ final class NetteInjectDetector
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! is_a($className, 'Nette\Application\IPresenter', true)) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// has parent class
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! $classReflection->isSubclassOf('Nette\Application\IPresenter')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// has no parent class
|
||||
if ($class->extends === null) {
|
||||
return false;
|
||||
}
|
||||
@ -78,18 +91,19 @@ final class NetteInjectDetector
|
||||
}
|
||||
|
||||
// prefer local constructor
|
||||
$classReflection = new ReflectionClass($className);
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
|
||||
if ($classReflection->hasMethod(MethodName::CONSTRUCT)) {
|
||||
/** @var ReflectionMethod $constructorReflectionMethod */
|
||||
$constructorReflectionMethod = $classReflection->getConstructor();
|
||||
$declaringClass = $constructorReflectionMethod->getDeclaringClass();
|
||||
|
||||
// be sure its local constructor
|
||||
if ($constructorReflectionMethod->class === $className) {
|
||||
if ($declaringClass->getName() === $className) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$classReflection = new ReflectionClass($parentClass);
|
||||
$classReflection = $this->reflectionProvider->getClass($parentClass);
|
||||
return $classReflection->hasMethod(MethodName::CONSTRUCT);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ namespace Rector\PostRector\Rector;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
|
||||
use Rector\CodingStyle\Node\NameImporter;
|
||||
@ -48,13 +49,19 @@ final class NameImportingPostRector extends NodeVisitorAbstract implements PostR
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ParameterProvider $parameterProvider,
|
||||
NameImporter $nameImporter,
|
||||
DocBlockNameImporter $docBlockNameImporter,
|
||||
ClassNameImportSkipper $classNameImportSkipper,
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
NodeNameResolver $nodeNameResolver
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
$this->nameImporter = $nameImporter;
|
||||
@ -62,6 +69,7 @@ final class NameImportingPostRector extends NodeVisitorAbstract implements PostR
|
||||
$this->classNameImportSkipper = $classNameImportSkipper;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): ?Node
|
||||
@ -102,15 +110,19 @@ final class NameImportingPostRector extends NodeVisitorAbstract implements PostR
|
||||
if (! is_callable($importName)) {
|
||||
return $this->nameImporter->importName($name);
|
||||
}
|
||||
|
||||
if (substr_count($name->toCodeString(), '\\') <= 1) {
|
||||
return $this->nameImporter->importName($name);
|
||||
}
|
||||
|
||||
if (! $this->classNameImportSkipper->isFoundInUse($name)) {
|
||||
return $this->nameImporter->importName($name);
|
||||
}
|
||||
if (function_exists($name->getLast())) {
|
||||
|
||||
if ($this->reflectionProvider->hasFunction(new Name($name->getLast()), null)) {
|
||||
return $this->nameImporter->importName($name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -92,12 +92,12 @@ final class UseAddingPostRector extends NodeVisitorAbstract implements PostRecto
|
||||
$this->useNodesToAddCollector->clear($smartFileInfo);
|
||||
|
||||
// A. has namespace? add under it
|
||||
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
|
||||
if ($namespace instanceof Namespace_) {
|
||||
$foundNode = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
|
||||
if ($foundNode instanceof Namespace_) {
|
||||
// first clean
|
||||
$this->useImportsRemover->removeImportsFromNamespace($namespace, $removedShortUses);
|
||||
$this->useImportsRemover->removeImportsFromNamespace($foundNode, $removedShortUses);
|
||||
// then add, to prevent adding + removing false positive of same short use
|
||||
$this->useImportsAdder->addImportsToNamespace($namespace, $useImportTypes, $functionUseImportTypes);
|
||||
$this->useImportsAdder->addImportsToNamespace($foundNode, $useImportTypes, $functionUseImportTypes);
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
@ -6,10 +6,15 @@ namespace Rector\ReadWrite\Guard;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Reflection\FunctionReflection;
|
||||
use PHPStan\Reflection\Native\NativeFunctionReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionFunction;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
|
||||
final class VariableToConstantGuard
|
||||
{
|
||||
@ -23,9 +28,24 @@ final class VariableToConstantGuard
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var PrivatesAccessor
|
||||
*/
|
||||
private $privatesAccessor;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
PrivatesAccessor $privatesAccessor
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->privatesAccessor = $privatesAccessor;
|
||||
}
|
||||
|
||||
public function isReadArg(Arg $arg): bool
|
||||
@ -35,40 +55,50 @@ final class VariableToConstantGuard
|
||||
return true;
|
||||
}
|
||||
|
||||
$functionName = $this->nodeNameResolver->getName($parentParent);
|
||||
if ($functionName === null) {
|
||||
$functionNameString = $this->nodeNameResolver->getName($parentParent);
|
||||
if ($functionNameString === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! function_exists($functionName)) {
|
||||
$functionName = new Name($functionNameString);
|
||||
$argScope = $arg->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
if (! $this->reflectionProvider->hasFunction($functionName, $argScope)) {
|
||||
// we don't know
|
||||
return true;
|
||||
}
|
||||
|
||||
$referenceParametersPositions = $this->resolveFunctionReferencePositions($functionName);
|
||||
$functionReflection = $this->reflectionProvider->getFunction($functionName, $argScope);
|
||||
|
||||
$referenceParametersPositions = $this->resolveFunctionReferencePositions($functionReflection);
|
||||
if ($referenceParametersPositions === []) {
|
||||
// no reference always only write
|
||||
return true;
|
||||
}
|
||||
|
||||
$argumentPosition = $this->getArgumentPosition($parentParent, $arg);
|
||||
|
||||
return ! in_array($argumentPosition, $referenceParametersPositions, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function resolveFunctionReferencePositions(string $functionName): array
|
||||
private function resolveFunctionReferencePositions(FunctionReflection $functionReflection): array
|
||||
{
|
||||
if (isset($this->referencePositionsByFunctionName[$functionName])) {
|
||||
return $this->referencePositionsByFunctionName[$functionName];
|
||||
if (isset($this->referencePositionsByFunctionName[$functionReflection->getName()])) {
|
||||
return $this->referencePositionsByFunctionName[$functionReflection->getName()];
|
||||
}
|
||||
|
||||
// this is needed, as native function reflection does not have access to referenced parameters
|
||||
if ($functionReflection instanceof NativeFunctionReflection) {
|
||||
$nativeFunctionReflection = new ReflectionFunction($functionReflection->getName());
|
||||
} else {
|
||||
$nativeFunctionReflection = $this->privatesAccessor->getPrivateProperty($functionReflection, 'reflection');
|
||||
}
|
||||
|
||||
$referencePositions = [];
|
||||
|
||||
$reflectionFunction = new ReflectionFunction($functionName);
|
||||
foreach ($reflectionFunction->getParameters() as $position => $reflectionParameter) {
|
||||
/** @var int $position */
|
||||
foreach ($nativeFunctionReflection->getParameters() as $position => $reflectionParameter) {
|
||||
if (! $reflectionParameter->isPassedByReference()) {
|
||||
continue;
|
||||
}
|
||||
@ -76,7 +106,7 @@ final class VariableToConstantGuard
|
||||
$referencePositions[] = $position;
|
||||
}
|
||||
|
||||
$this->referencePositionsByFunctionName[$functionName] = $referencePositions;
|
||||
$this->referencePositionsByFunctionName[$functionReflection->getName()] = $referencePositions;
|
||||
|
||||
return $referencePositions;
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ final class NodeTypesProvider
|
||||
public function provide(): array
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder = $finder
|
||||
->files()
|
||||
$finder = $finder->files()
|
||||
->in(self::PHP_PARSER_NODES_PATH);
|
||||
|
||||
$fileInfos = iterator_to_array($finder->getIterator());
|
||||
@ -44,6 +43,7 @@ final class NodeTypesProvider
|
||||
if ($reflectionClass->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($reflectionClass->isInterface()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -32,15 +32,16 @@ final class PhpParserNodeMapper
|
||||
if (! is_a($node, $phpParserNodeMapper->getNodeType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// do not let Expr collect all the types
|
||||
// note: can be solve later with priorities on mapper interface, making this last
|
||||
if ($phpParserNodeMapper->getNodeType() !== Expr::class) {
|
||||
return $phpParserNodeMapper->mapToPHPStan($node);
|
||||
}
|
||||
if (! is_a($node, String_::class)) {
|
||||
|
||||
if (! $node instanceof String_) {
|
||||
return $phpParserNodeMapper->mapToPHPStan($node);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new NotImplementedYetException(get_class($node));
|
||||
|
@ -6,6 +6,7 @@ namespace Rector\StaticTypeMapper\PhpParser;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\FloatType;
|
||||
@ -15,7 +16,6 @@ use PHPStan\Type\StaticType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
@ -29,9 +29,17 @@ final class NameNodeMapper implements PhpParserNodeMapperInterface
|
||||
*/
|
||||
private $renamedClassesCollector;
|
||||
|
||||
public function __construct(RenamedClassesCollector $renamedClassesCollector)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
RenamedClassesCollector $renamedClassesCollector,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->renamedClassesCollector = $renamedClassesCollector;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
@ -58,7 +66,7 @@ final class NameNodeMapper implements PhpParserNodeMapperInterface
|
||||
|
||||
private function isExistingClass(string $name): bool
|
||||
{
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($name)) {
|
||||
if ($this->reflectionProvider->hasClass($name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -79,6 +87,11 @@ final class NameNodeMapper implements PhpParserNodeMapperInterface
|
||||
return new StaticType($className);
|
||||
}
|
||||
|
||||
if ($this->reflectionProvider->hasClass($className)) {
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
return new ThisType($classReflection);
|
||||
}
|
||||
|
||||
return new ThisType($className);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ use Rector\Core\NonPhpFile\NonPhpFileProcessor;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\Core\Stubs\StubLoader;
|
||||
use Rector\Core\ValueObject\StaticNonPhpFileSuffixes;
|
||||
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider;
|
||||
use Rector\Testing\Application\EnabledRectorClassProvider;
|
||||
use Rector\Testing\Configuration\AllRectorConfigFactory;
|
||||
use Rector\Testing\Contract\RunnableInterface;
|
||||
@ -87,11 +88,6 @@ abstract class AbstractRectorTestCase extends AbstractKernelTestCase
|
||||
*/
|
||||
private static $runnableRectorFactory;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $autoloadTestFixture = true;
|
||||
|
||||
/**
|
||||
* @var RectorConfigsResolver
|
||||
*/
|
||||
@ -154,13 +150,6 @@ abstract class AbstractRectorTestCase extends AbstractKernelTestCase
|
||||
return StaticFixtureFinder::yieldDirectory($directory, $suffix);
|
||||
}
|
||||
|
||||
protected function doTestFileInfoWithoutAutoload(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->autoloadTestFixture = false;
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
$this->autoloadTestFixture = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $extraFileInfos
|
||||
*/
|
||||
@ -170,7 +159,7 @@ abstract class AbstractRectorTestCase extends AbstractKernelTestCase
|
||||
|
||||
$inputFileInfoAndExpectedFileInfo = StaticFixtureSplitter::splitFileInfoToLocalInputAndExpectedFileInfos(
|
||||
$fixtureFileInfo,
|
||||
$this->autoloadTestFixture
|
||||
false
|
||||
);
|
||||
|
||||
$inputFileInfo = $inputFileInfoAndExpectedFileInfo->getInputFileInfo();
|
||||
@ -180,6 +169,10 @@ abstract class AbstractRectorTestCase extends AbstractKernelTestCase
|
||||
$nodeScopeResolver = $this->getService(NodeScopeResolver::class);
|
||||
$nodeScopeResolver->setAnalysedFiles([$inputFileInfo->getRealPath()]);
|
||||
|
||||
/** @var DynamicSourceLocatorProvider $dynamicDirectoryLocatorProvider */
|
||||
$dynamicDirectoryLocatorProvider = $this->getService(DynamicSourceLocatorProvider::class);
|
||||
$dynamicDirectoryLocatorProvider->setFileInfo($inputFileInfo);
|
||||
|
||||
$expectedFileInfo = $inputFileInfoAndExpectedFileInfo->getExpectedFileInfo();
|
||||
|
||||
$this->doTestFileMatchesExpectedContent($inputFileInfo, $expectedFileInfo, $fixtureFileInfo, $extraFileInfos);
|
||||
|
@ -4,22 +4,13 @@ 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\NodeManipulator\ClassManipulator;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
abstract class AbstractNodeVendorLockResolver
|
||||
{
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
protected $classManipulator;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
@ -33,80 +24,53 @@ abstract class AbstractNodeVendorLockResolver
|
||||
/**
|
||||
* @var FamilyRelationsAnalyzer
|
||||
*/
|
||||
private $familyRelationsAnalyzer;
|
||||
protected $familyRelationsAnalyzer;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireAbstractNodeVendorLockResolver(
|
||||
NodeRepository $nodeRepository,
|
||||
ClassManipulator $classManipulator,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
FamilyRelationsAnalyzer $familyRelationsAnalyzer
|
||||
): void {
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
|
||||
}
|
||||
|
||||
protected function hasParentClassChildrenClassesOrImplementsInterface(ClassLike $classLike): bool
|
||||
protected function hasParentClassChildrenClassesOrImplementsInterface(ClassReflection $classReflection): bool
|
||||
{
|
||||
if (($classLike instanceof Class_ || $classLike instanceof Interface_) && $classLike->extends) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classLike instanceof Class_) {
|
||||
if ((bool) $classLike->implements) {
|
||||
if ($classReflection->isClass()) {
|
||||
// has at least interface
|
||||
if ($classReflection->getInterfaces() !== []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Class_ $classLike */
|
||||
return $this->getChildrenClassesByClass($classLike) !== [];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Interface_ $classLike
|
||||
*/
|
||||
protected function isMethodVendorLockedByInterface(ClassLike $classLike, string $methodName): bool
|
||||
{
|
||||
$interfaceNames = $this->classManipulator->getClassLikeNodeParentInterfaceNames($classLike);
|
||||
|
||||
foreach ($interfaceNames as $interfaceName) {
|
||||
if (! $this->hasInterfaceMethod($methodName, $interfaceName)) {
|
||||
continue;
|
||||
// has at least one parent class
|
||||
if ($classReflection->getParents() !== []) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
return $childrenClassReflections !== [];
|
||||
}
|
||||
|
||||
if ($classReflection->isInterface()) {
|
||||
return $classReflection->getInterfaces() !== [];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string[]
|
||||
*/
|
||||
protected function getChildrenClassesByClass(Class_ $class): array
|
||||
protected function isMethodVendorLockedByInterface(ClassReflection $classReflection, string $methodName): bool
|
||||
{
|
||||
$desiredClassName = $class->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($desiredClassName === null) {
|
||||
return [];
|
||||
foreach ($classReflection->getInterfaces() as $interfaceReflection) {
|
||||
if ($interfaceReflection->hasMethod($methodName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->familyRelationsAnalyzer->getChildrenOfClass($desiredClassName);
|
||||
}
|
||||
|
||||
private function hasInterfaceMethod(string $methodName, string $interfaceName): bool
|
||||
{
|
||||
if (! interface_exists($interfaceName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$interfaceMethods = get_class_methods($interfaceName);
|
||||
|
||||
return in_array($methodName, $interfaceMethods, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,49 +4,59 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class ClassMethodParamVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
public function isVendorLocked(ClassMethod $classMethod, int $paramPosition): bool
|
||||
{
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classNode instanceof Class_) {
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classNode)) {
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classReflection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
if ($parentClassName !== null) {
|
||||
$vendorLock = $this->isParentClassVendorLocking($paramPosition, $parentClassName, $methodName);
|
||||
if ($classReflection->getParentClass() !== false) {
|
||||
$vendorLock = $this->isParentClassVendorLocking(
|
||||
$classReflection->getParentClass(),
|
||||
$paramPosition,
|
||||
$methodName
|
||||
);
|
||||
if ($vendorLock !== null) {
|
||||
return $vendorLock;
|
||||
}
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode instanceof Class_) {
|
||||
return $this->isMethodVendorLockedByInterface($classNode, $methodName);
|
||||
if ($classReflection->isClass()) {
|
||||
return $this->isMethodVendorLockedByInterface($classReflection, $methodName);
|
||||
}
|
||||
if ($classNode instanceof Interface_) {
|
||||
return $this->isMethodVendorLockedByInterface($classNode, $methodName);
|
||||
|
||||
if ($classReflection->isInterface()) {
|
||||
return $this->isMethodVendorLockedByInterface($classReflection, $methodName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isParentClassVendorLocking(int $paramPosition, string $parentClassName, string $methodName): ?bool
|
||||
{
|
||||
$parentClass = $this->nodeRepository->findClass($parentClassName);
|
||||
private function isParentClassVendorLocking(
|
||||
ClassReflection $parentClassReflection,
|
||||
int $paramPosition,
|
||||
string $methodName
|
||||
): ?bool {
|
||||
$parentClass = $this->nodeRepository->findClass($parentClassReflection->getName());
|
||||
if ($parentClass !== null) {
|
||||
$parentClassMethod = $parentClass->getMethod($methodName);
|
||||
// parent class method in local scope → it's ok
|
||||
@ -58,7 +68,8 @@ final class ClassMethodParamVendorLockResolver extends AbstractNodeVendorLockRes
|
||||
return $parentClassMethod->params[$paramPosition]->type === null;
|
||||
}
|
||||
}
|
||||
if (method_exists($parentClassName, $methodName)) {
|
||||
|
||||
if ($parentClassReflection->hasMethod($methodName)) {
|
||||
// parent class method in external scope → it's not ok
|
||||
// if not, look for it's parent parent
|
||||
return true;
|
||||
|
@ -6,17 +6,20 @@ namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
//use PHPStan\Analyser\Scope;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\NodeVisitor;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
@ -24,10 +27,10 @@ use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
final class ClassMethodReturnTypeOverrideGuard
|
||||
{
|
||||
/**
|
||||
* @var array<string, array<string>>
|
||||
* @var array<class-string, array<string>>
|
||||
*/
|
||||
private const CHAOTIC_CLASS_METHOD_NAMES = [
|
||||
NodeVisitor::class => ['enterNode', 'leaveNode', 'beforeTraverse', 'afterTraverse'],
|
||||
'PhpParser\NodeVisitor' => ['enterNode', 'leaveNode', 'beforeTraverse', 'afterTraverse'],
|
||||
];
|
||||
|
||||
/**
|
||||
@ -41,9 +44,14 @@ final class ClassMethodReturnTypeOverrideGuard
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var NodeRepository
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $nodeRepository;
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var FamilyRelationsAnalyzer
|
||||
*/
|
||||
private $familyRelationsAnalyzer;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
@ -53,19 +61,21 @@ final class ClassMethodReturnTypeOverrideGuard
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
NodeRepository $nodeRepository,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
FamilyRelationsAnalyzer $familyRelationsAnalyzer,
|
||||
BetterNodeFinder $betterNodeFinder
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
|
||||
public function shouldSkipClassMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
// 1. skip magic methods
|
||||
if ($this->nodeNameResolver->isName($classMethod->name, '__*')) {
|
||||
if ($classMethod->isMagic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -74,24 +84,22 @@ final class ClassMethodReturnTypeOverrideGuard
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. skip has children and current has no return
|
||||
$class = $classMethod->getAttribute(AttributeKey::PARENT_NODE);
|
||||
$hasChildren = $class instanceof Class_ && $this->nodeRepository->hasClassChildren($class);
|
||||
if (! $hasChildren) {
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasReturn = (bool) $this->betterNodeFinder->findFirst(
|
||||
(array) $classMethod->stmts,
|
||||
function (Node $node): bool {
|
||||
if (! $node instanceof Return_) {
|
||||
return false;
|
||||
}
|
||||
return $node->expr instanceof Expr;
|
||||
}
|
||||
);
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
if ($hasReturn) {
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
if ($childrenClassReflections === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->hasClassMethodExprReturn($classMethod)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -119,15 +127,27 @@ final class ClassMethodReturnTypeOverrideGuard
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::CHAOTIC_CLASS_METHOD_NAMES as $chaoticClass => $chaoticMethodNames) {
|
||||
if (! is_a($className, $chaoticClass, true)) {
|
||||
if (! $this->reflectionProvider->hasClass($chaoticClass)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return in_array($methodName, $chaoticMethodNames, true);
|
||||
$chaoticClassReflection = $this->reflectionProvider->getClass($chaoticClass);
|
||||
if (! $classReflection->isSubclassOf($chaoticClassReflection->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->nodeNameResolver->isNames($classMethod, $chaoticMethodNames);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -172,4 +192,15 @@ final class ClassMethodReturnTypeOverrideGuard
|
||||
|
||||
return $isMatchingClassTypes;
|
||||
}
|
||||
|
||||
private function hasClassMethodExprReturn(ClassMethod $classMethod): bool
|
||||
{
|
||||
return (bool) $this->betterNodeFinder->findFirst($classMethod->getStmts(), function (Node $node): bool {
|
||||
if (! $node instanceof Return_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $node->expr instanceof Expr;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,60 +4,60 @@ declare(strict_types=1);
|
||||
|
||||
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 PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\FunctionVariantWithPhpDocs;
|
||||
use PHPStan\Type\MixedType;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class ClassMethodReturnVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
public function isVendorLocked(ClassMethod $classMethod): bool
|
||||
{
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classNode instanceof ClassLike) {
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classNode)) {
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classReflection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classMethod->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
if ($parentClassName !== null) {
|
||||
return $this->isVendorLockedByParentClass($parentClassName, $methodName);
|
||||
if ($this->isVendorLockedByParentClass($classReflection, $methodName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classNode instanceof Class_) {
|
||||
return $this->isMethodVendorLockedByInterface($classNode, $methodName);
|
||||
if ($classReflection->isTrait()) {
|
||||
return false;
|
||||
}
|
||||
if ($classNode instanceof Interface_) {
|
||||
return $this->isMethodVendorLockedByInterface($classNode, $methodName);
|
||||
}
|
||||
return false;
|
||||
|
||||
return $this->isMethodVendorLockedByInterface($classReflection, $methodName);
|
||||
}
|
||||
|
||||
private function isVendorLockedByParentClass(string $parentClassName, string $methodName): bool
|
||||
private function isVendorLockedByParentClass(ClassReflection $classReflection, string $methodName): bool
|
||||
{
|
||||
$parentClass = $this->nodeRepository->findClass($parentClassName);
|
||||
if ($parentClass !== null) {
|
||||
$parentClassMethod = $parentClass->getMethod($methodName);
|
||||
// validate type is conflicting
|
||||
// parent class method in local scope → it's ok
|
||||
if ($parentClassMethod !== null) {
|
||||
return $parentClassMethod->returnType !== null;
|
||||
foreach ($classReflection->getParents() as $parentClassReflections) {
|
||||
if (! $parentClassReflections->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent
|
||||
$parentClassMethodReflection = $parentClassReflections->getNativeMethod($methodName);
|
||||
$parametersAcceptor = $parentClassMethodReflection->getVariants()[0];
|
||||
if (! $parametersAcceptor instanceof FunctionVariantWithPhpDocs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// here we count only on strict types, not on docs
|
||||
return ! $parametersAcceptor->getNativeReturnType() instanceof MixedType;
|
||||
}
|
||||
|
||||
// validate type is conflicting
|
||||
// parent class method in external scope → it's not ok
|
||||
return method_exists($parentClassName, $methodName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionClass;
|
||||
|
||||
final class ClassMethodVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
@ -22,45 +22,33 @@ final class ClassMethodVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
*/
|
||||
public function isRemovalVendorLocked(ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var string $classMethodName */
|
||||
$classMethodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
/** @var Class_|Interface_|null $classLike */
|
||||
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($classLike === null) {
|
||||
/** @var Scope $scope */
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isMethodVendorLockedByInterface($classLike, $classMethodName)) {
|
||||
if ($this->isMethodVendorLockedByInterface($classReflection, $classMethodName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classLike->extends === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
/** @var string[] $classParents */
|
||||
$classParents = (array) class_parents($className);
|
||||
|
||||
foreach ($classParents as $classParent) {
|
||||
if (! class_exists($classParent)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClassReflection = new ReflectionClass($classParent);
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if (! $parentClassReflection->hasMethod($classMethodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$methodReflection = $parentClassReflection->getMethod($classMethodName);
|
||||
if (! $methodReflection->isAbstract()) {
|
||||
$methodReflection = $parentClassReflection->getNativeMethod($classMethodName);
|
||||
if (! $methodReflection instanceof PhpMethodReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
if ($methodReflection->isAbstract()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Privatization\VisibilityGuard\ClassMethodVisibilityGuard;
|
||||
|
||||
@ -25,64 +27,21 @@ final class ClassMethodVisibilityVendorLockResolver extends AbstractNodeVendorLo
|
||||
*/
|
||||
public function isParentLockedMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
/** @var Scope $scope */
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
if ($this->isInterfaceMethod($classMethod, $className)) {
|
||||
return true;
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
return $this->hasParentMethod($className, $methodName);
|
||||
}
|
||||
/** @var ClassReflection[] $parentClassReflections */
|
||||
$parentClassReflections = array_merge($classReflection->getParents(), $classReflection->getInterfaces());
|
||||
|
||||
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
|
||||
{
|
||||
$interfaceMethodNames = $this->getInterfaceMethodNames($className);
|
||||
return $this->nodeNameResolver->isNames($classMethod, $interfaceMethodNames);
|
||||
}
|
||||
|
||||
private function hasParentMethod(string $className, string $methodName): bool
|
||||
{
|
||||
/** @var string[] $parentClasses */
|
||||
$parentClasses = (array) 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)) {
|
||||
foreach ($parentClassReflections as $parentClassReflection) {
|
||||
if ($parentClassReflection->hasMethod($methodName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -90,20 +49,25 @@ final class ClassMethodVisibilityVendorLockResolver extends AbstractNodeVendorLo
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getInterfaceMethodNames(string $className): array
|
||||
public function isChildLockedMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var string[] $interfaces */
|
||||
$interfaces = (array) class_implements($className);
|
||||
/** @var Scope $scope */
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$interfaceMethods = [];
|
||||
foreach ($interfaces as $interface) {
|
||||
$currentInterfaceMethods = get_class_methods($interface);
|
||||
$interfaceMethods = array_merge($interfaceMethods, $currentInterfaceMethods);
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $interfaceMethods;
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
foreach ($childrenClassReflections as $childClassReflection) {
|
||||
if ($childClassReflection->hasMethod($methodName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,94 +4,79 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionProperty;
|
||||
|
||||
final class PropertyTypeVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
{
|
||||
public function isVendorLocked(Property $property): bool
|
||||
{
|
||||
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classLike instanceof Class_) {
|
||||
/** @var Scope $scope */
|
||||
$scope = $property->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var Class_|Interface_ $classLike */
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classLike)) {
|
||||
if (! $this->hasParentClassChildrenClassesOrImplementsInterface($classReflection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
if ($this->isParentClassLocked($classLike, $propertyName)) {
|
||||
if ($this->isParentClassLocked($classReflection, $propertyName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isChildClassLocked($property, $classLike, $propertyName);
|
||||
return $this->isChildClassLocked($property, $classReflection, $propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Interface_ $classLike
|
||||
*/
|
||||
private function isParentClassLocked(ClassLike $classLike, string $propertyName): bool
|
||||
private function isParentClassLocked(ClassReflection $classReflection, string $propertyName): bool
|
||||
{
|
||||
if (! $classLike instanceof Class_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract to some "inherited parent method" service
|
||||
/** @var string|null $parentClassName */
|
||||
$parentClassName = $classLike->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
if ($parentClassName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not, look for it's parent parent - recursion
|
||||
|
||||
if (property_exists($parentClassName, $propertyName)) {
|
||||
// validate type is conflicting
|
||||
// parent class property in external scope → it's not ok
|
||||
return true;
|
||||
|
||||
// if not, look for it's parent parent
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if ($parentClassReflection->hasProperty($propertyName)) {
|
||||
// validate type is conflicting
|
||||
// parent class property in external scope → it's not ok
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|Interface_ $classLike
|
||||
*/
|
||||
private function isChildClassLocked(Property $property, ClassLike $classLike, string $propertyName): bool
|
||||
{
|
||||
if (! $classLike instanceof Class_) {
|
||||
private function isChildClassLocked(
|
||||
Property $property,
|
||||
ClassReflection $classReflection,
|
||||
string $propertyName
|
||||
): bool {
|
||||
if (! $classReflection->isClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// is child class locker
|
||||
// is child class locked?
|
||||
if ($property->isPrivate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$childrenClassNames = $this->getChildrenClassesByClass($classLike);
|
||||
$childClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
|
||||
foreach ($childrenClassNames as $childClassName) {
|
||||
if (! property_exists($childClassName, $propertyName)) {
|
||||
foreach ($childClassReflections as $childClassReflection) {
|
||||
if (! $childClassReflection->hasProperty($propertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$propertyReflection = $childClassReflection->getNativeProperty($propertyName);
|
||||
|
||||
// ensure the property is not in the parent class
|
||||
$reflectionProperty = new ReflectionProperty($childClassName, $propertyName);
|
||||
if ($reflectionProperty->class !== $childClassName) {
|
||||
continue;
|
||||
$propertyReflectionDeclaringClass = $propertyReflection->getDeclaringClass();
|
||||
if ($propertyReflectionDeclaringClass->getName() === $childClassReflection->getName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -4,7 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class PropertyVisibilityVendorLockResolver extends AbstractNodeVendorLockResolver
|
||||
@ -18,58 +21,52 @@ final class PropertyVisibilityVendorLockResolver extends AbstractNodeVendorLockR
|
||||
*/
|
||||
public function isParentLockedProperty(Property $property): bool
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $property->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
return $this->hasParentProperty($className, $propertyName);
|
||||
}
|
||||
|
||||
public function isChildLockedProperty(Property $property): bool
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $property->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
/** @var string $propertyName */
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
return $this->hasChildProperty($className, $propertyName);
|
||||
}
|
||||
|
||||
private function hasParentProperty(string $className, string $propertyName): bool
|
||||
{
|
||||
/** @var string[] $parentClasses */
|
||||
$parentClasses = (array) class_parents($className);
|
||||
|
||||
foreach ($parentClasses as $parentClass) {
|
||||
if (! property_exists($parentClass, $propertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
$classReflection = $this->resolveClassReflection($property);
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
private function hasChildProperty(string $desiredClassName, string $propertyName): bool
|
||||
{
|
||||
foreach (get_declared_classes() as $className) {
|
||||
if ($className === $desiredClassName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! is_a($className, $desiredClassName, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property_exists($className, $propertyName)) {
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if ($parentClassReflection->hasProperty($propertyName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isChildLockedProperty(Property $property): bool
|
||||
{
|
||||
$classReflection = $this->resolveClassReflection($property);
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$propertyName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
foreach ($childrenClassReflections as $childClassReflection) {
|
||||
if ($childClassReflection === $classReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($childClassReflection->hasProperty($propertyName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function resolveClassReflection(Node $node): ?ClassReflection
|
||||
{
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $scope->getClassReflection();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
parameters:
|
||||
inferPrivatePropertyTypeFromConstructor: true
|
||||
|
||||
scanDirectories:
|
||||
- stubs
|
||||
|
||||
includes:
|
||||
- utils/phpstan-extensions/config/phpstan-extensions.neon
|
||||
- vendor/phpstan/phpstan-nette/extension.neon
|
||||
|
93
phpstan.neon
93
phpstan.neon
@ -121,10 +121,11 @@ parameters:
|
||||
|
||||
|
||||
- '#Cognitive complexity for "Rector\\Php80\\NodeResolver\\SwitchExprsResolver\:\:resolve\(\)" is \d+, keep it under 9#'
|
||||
|
||||
-
|
||||
message: "#^Cognitive complexity for \"Rector\\\\PhpSpecToPHPUnit\\\\Rector\\\\MethodCall\\\\PhpSpecPromisesToPHPUnitAssertRector\\:\\:refactor\\(\\)\" is 13, keep it under 9$#"
|
||||
count: 1
|
||||
path: rules/php-spec-to-phpunit/src/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php
|
||||
|
||||
-
|
||||
message: '#Class cognitive complexity is \d+, keep it under \d+#'
|
||||
paths:
|
||||
@ -133,6 +134,7 @@ parameters:
|
||||
- packages/node-type-resolver/src/NodeTypeResolver.php
|
||||
- rules/code-quality-strict/src/Rector/Variable/MoveVariableDeclarationNearReferenceRector.php
|
||||
- rules/php80/src/Rector/If_/NullsafeOperatorRector.php
|
||||
|
||||
- "#^Cognitive complexity for \"Rector\\\\Php70\\\\EregToPcreTransformer\\:\\:(.*?)\" is (.*?), keep it under 9$#"
|
||||
- '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\If_\\SimplifyIfIssetToNullCoalescingRector\:\:shouldSkip\(\)" is 10, keep it under 9#'
|
||||
- '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\Type\\ObjectTypeSpecifier\:\:matchShortenedObjectType\(\)" is 10, keep it under 9#'
|
||||
@ -158,8 +160,6 @@ parameters:
|
||||
message: '#Class "Rector\\RectorGenerator\\Rector\\Closure\\AddNewServiceToSymfonyPhpConfigRector" is missing @see annotation with test case class reference#'
|
||||
path: 'packages/rector-generator/src/Rector/Closure/AddNewServiceToSymfonyPhpConfigRector.php'
|
||||
|
||||
- '#Class Rector\\Renaming\\Tests\\Rector\\MethodCall\\RenameMethodRector\\Fixture\\SkipSelfMethodRename not found#'
|
||||
|
||||
- '#Parameter \#1 \$variable of class Rector\\Php70\\ValueObject\\VariableAssignPair constructor expects PhpParser\\Node\\Expr\\ArrayDimFetch\|PhpParser\\Node\\Expr\\PropertyFetch\|PhpParser\\Node\\Expr\\StaticPropertyFetch\|PhpParser\\Node\\Expr\\Variable, PhpParser\\Node\\Expr given#'
|
||||
|
||||
# is nested expr
|
||||
@ -319,8 +319,6 @@ parameters:
|
||||
|
||||
###################################################
|
||||
|
||||
- '#should return ReflectionClassConstant\|null but returns ReflectionClassConstant\|false#'
|
||||
|
||||
- '#Method "evaluateBinaryToVersionCompareCondition\(\)" returns bool type, so the name should start with is/has/was#'
|
||||
|
||||
# soo many false positive naming - fix later with Recto rule
|
||||
@ -404,12 +402,6 @@ parameters:
|
||||
- rules/naming/src/Guard/PropertyConflictingNameGuard/AbstractPropertyConflictingNameGuard.php
|
||||
- rules/naming/src/PropertyRenamer/AbstractPropertyRenamer.php
|
||||
|
||||
-
|
||||
message: '#".*" regex need to use string named capture group instead of numeric#'
|
||||
paths:
|
||||
- packages/better-php-doc-parser/src/PhpDocParser/BetterPhpDocParser.php #268
|
||||
- rules/php70/src/EregToPcreTransformer.php #277
|
||||
|
||||
-
|
||||
message: '#There should be no empty class#'
|
||||
paths:
|
||||
@ -512,7 +504,6 @@ parameters:
|
||||
# known values
|
||||
- '#Method Rector\\Testing\\Finder\\RectorsFinder\:\:findClassesInDirectoriesByName\(\) should return array<class\-string\> but returns array<int, \(int\|string\)\>#'
|
||||
- '#Property PhpParser\\Node\\Param\:\:\$type \(PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType\|null\) does not accept PhpParser\\Node#'
|
||||
- '#Parameter \#1 \$interfaces of method Rector\\PHPStanStaticTypeMapper\\TypeAnalyzer\\UnionTypeCommonTypeNarrower\:\:filterOutNativeInterfaces\(\) expects array<class\-string\>, array<string, string\> given#'
|
||||
- '#Content of method "getFunctionLikePhpDocInfo\(\)" is duplicated with method "getFunctionLikePhpDocInfo\(\)" in "Rector\\TypeDeclaration\\TypeInferer\\ParamTypeInferer\\PHPUnitDataProviderParamTypeInferer" class\. Use unique content or abstract service instead#'
|
||||
|
||||
-
|
||||
@ -528,3 +519,81 @@ parameters:
|
||||
- '#Property Rector\\Core\\PhpParser\\Node\\AssignAndBinaryMap\:\:\$binaryOpToAssignClasses \(array<class\-string<PhpParser\\Node\\Expr\\BinaryOp\>, class\-string<PhpParser\\Node\\Expr\\BinaryOp\>\>\) does not accept array#'
|
||||
- '#Content of method "configure\(\)" is duplicated with method "configure\(\)" in "Rector\\RemovingStatic\\Rector\\Class_\\NewUniqueObjectToEntityFactoryRector" class\. Use unique content or abstract service instead#'
|
||||
- '#Content of method "configure\(\)" is duplicated with method "configure\(\)" in "Rector\\RemovingStatic\\Rector\\Class_\\PassFactoryToUniqueObjectRector" class\. Use unique content or abstract service instead#'
|
||||
|
||||
-
|
||||
message: '#Function "class_exists\(\)" cannot be used/left in the code#'
|
||||
paths:
|
||||
- utils/phpstan-type-mapper-checker/src/Finder/PHPStanTypeClassFinder.php
|
||||
- packages/testing/src/Finder/RectorsFinder.php
|
||||
- packages/better-php-doc-parser/src/AnnotationReader/AnnotationReaderFactory.php
|
||||
|
||||
-
|
||||
message: '#Function "property_exists\(\)" cannot be used/left in the code#'
|
||||
paths:
|
||||
# on PhpParser Nodes
|
||||
- src/NodeManipulator/ClassMethodAssignManipulator.php
|
||||
- rules/php80/src/Rector/If_/NullsafeOperatorRector.php
|
||||
- packages/node-type-resolver/src/NodeVisitor/FunctionMethodAndClassNodeVisitor.php
|
||||
- packages/node-name-resolver/src/NodeNameResolver.php
|
||||
- packages/node-type-resolver/src/PHPStan/Scope/PHPStanNodeScopeResolver.php
|
||||
- packages/node-name-resolver/src/NodeNameResolver/ClassNameResolver.php
|
||||
- packages/node-type-resolver/src/NodeVisitor/StatementNodeVisitor.php
|
||||
- packages/better-php-doc-parser/src/Printer/PhpDocInfoPrinter.php
|
||||
- packages/better-php-doc-parser/src/Printer/MultilineSpaceFormatPreserver.php
|
||||
|
||||
-
|
||||
message: '#Function "class_implements\(\)" cannot be used/left in the code#'
|
||||
paths:
|
||||
- rules/symfony/src/ValueObject/ServiceMap/ServiceMap.php
|
||||
|
||||
-
|
||||
message: '#Instead of "ReflectionClass" class/interface use "PHPStan\\Reflection\\ClassReflection"#'
|
||||
paths:
|
||||
# own classes
|
||||
- packages/rector-generator/src/Provider/SetsListProvider.php
|
||||
- packages/testing/src/Finder/RectorsFinder.php
|
||||
- packages/static-type-mapper/src/TypeFactory/UnionTypeFactory.php
|
||||
- packages/set/src/RectorSetProvider.php
|
||||
- packages/rector-generator/src/Provider/NodeTypesProvider.php
|
||||
|
||||
- '#Method Rector\\TypeDeclaration\\PhpParserTypeAnalyzer\:\:unwrapNullableAndToString\(\) should return string but returns string\|null#'
|
||||
|
||||
# @todo resolve in next PR!!! (Feb 2021)
|
||||
- '#Function "array_filter\(\)" cannot be used/left in the code#'
|
||||
- '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\Isset_\\IssetOnPropertyObjectToPropertyExistsRector\:\:refactor\(\)" is 11, keep it under 9#'
|
||||
- '#Cognitive complexity for "Rector\\DeadCode\\UnusedNodeResolver\\ClassUnusedPrivateClassMethodResolver\:\:filterOutParentAbstractMethods\(\)" is 10, keep it under 9#'
|
||||
|
||||
# known types
|
||||
- '#Call to an undefined method PHPStan\\Type\\ConstantType\:\:getValue\(\)#'
|
||||
- '#Method Rector\\BetterPhpDocParser\\AnnotationReader\\NodeAnnotationReader\:\:getNativePropertyReflection\(\) should return ReflectionProperty\|null but return statement is missing#'
|
||||
- '#Parameter \#1 \$node of method Rector\\NetteKdyby\\Naming\\VariableNaming\:\:resolveFromMethodCall\(\) expects PhpParser\\Node\\Expr\\MethodCall\|PhpParser\\Node\\Expr\\NullsafeMethodCall\|PhpParser\\Node\\Expr\\StaticCall, PhpParser\\Node given#'
|
||||
- '#Parameter \#1 \$stmts of method Rector\\EarlyReturn\\Rector\\Return_\\PreparedValueToEarlyReturnRector\:\:collectIfs\(\) expects array<PhpParser\\Node\\Stmt\\If_\>, array<PhpParser\\Node\\Stmt\> given#'
|
||||
- '#Access to an undefined property PhpParser\\Node\\FunctionLike\|PhpParser\\Node\\Stmt\\If_\:\:\$stmts#'
|
||||
- '#Method Rector\\NetteKdyby\\DataProvider\\EventAndListenerTreeProvider\:\:getListeningClassMethodsInEventSubscriberByClass\(\) should return array<class\-string, array<PhpParser\\Node\\Stmt\\ClassMethod\>\> but returns array<string, array<PhpParser\\Node\\Stmt\\ClassMethod\>\>#'
|
||||
|
||||
-
|
||||
message: '#Function "get_declared_classes\(\)" cannot be used/left in the code#'
|
||||
paths:
|
||||
- rules/restoration/src/ClassMap/ExistingClassesProvider.php
|
||||
|
||||
# false positives
|
||||
-
|
||||
message: '#Empty array passed to foreach#'
|
||||
paths:
|
||||
- rules/transform/src/Rector/New_/NewToConstructorInjectionRector.php
|
||||
-
|
||||
message: '#Parameter \#1 \$types of method Rector\\Defluent\\NodeAnalyzer\\FluentCallStaticTypeResolver\:\:filterOutAlreadyPresentParentClasses\(\) expects array<class\-string\>, array<int, string\> given#'
|
||||
paths:
|
||||
- rules/defluent/src/NodeAnalyzer/FluentCallStaticTypeResolver.php
|
||||
|
||||
- '#Cognitive complexity for "Rector\\CodeQuality\\Rector\\Isset_\\IssetOnPropertyObjectToPropertyExistsRector\:\:refactor\(\)" is \d+, keep it under 9#'
|
||||
|
||||
# needed for native reflection parameter reference
|
||||
-
|
||||
message: '#Instead of "ReflectionFunction" class/interface use "PHPStan\\Reflection\\FunctionReflection"#'
|
||||
paths:
|
||||
- packages/read-write/src/Guard/VariableToConstantGuard.php
|
||||
|
||||
- '#Cognitive complexity for "Rector\\NetteCodeQuality\\FormControlTypeResolver\\MagicNetteFactoryInterfaceFormControlTypeResolver\:\:resolve\(\)" is 11, keep it under 9#'
|
||||
- '#Cognitive complexity for "Rector\\NetteCodeQuality\\FormControlTypeResolver\\MagicNetteFactoryInterfaceFormControlTypeResolver\:\:resolve\(\)" is 12, keep it under 9#'
|
||||
|
||||
|
@ -8,8 +8,8 @@ use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchR
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\PhpVersion;
|
||||
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector;
|
||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||
use Rector\Privatization\Rector\Property\PrivatizeLocalPropertyToPrivatePropertyRector;
|
||||
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
@ -74,8 +74,10 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters->set(Option::SKIP, [
|
||||
StringClassNameToClassConstantRector::class,
|
||||
SplitStringClassConstantToClassConstFetchRector::class,
|
||||
// false positives on constants used in rector.php
|
||||
RemoveUnusedClassConstantRector::class,
|
||||
|
||||
PrivatizeLocalPropertyToPrivatePropertyRector::class => [
|
||||
__DIR__ . '/src/Rector/AbstractTemporaryRector.php',
|
||||
],
|
||||
|
||||
// test paths
|
||||
'*/Fixture/*',
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Rector\CakePHP\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\CakePHP\ImplicitNameResolver;
|
||||
|
||||
/**
|
||||
@ -35,9 +36,15 @@ final class CakePHPFullyQualifiedClassNameResolver
|
||||
*/
|
||||
private $implicitNameResolver;
|
||||
|
||||
public function __construct(ImplicitNameResolver $implicitNameResolver)
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ImplicitNameResolver $implicitNameResolver, ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->implicitNameResolver = $implicitNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,10 +70,7 @@ final class CakePHPFullyQualifiedClassNameResolver
|
||||
|
||||
// B. is Cake native class?
|
||||
$cakePhpVersion = 'Cake\\' . $pseudoNamespace . '\\' . $shortClass;
|
||||
if (class_exists($cakePhpVersion)) {
|
||||
return $cakePhpVersion;
|
||||
}
|
||||
if (interface_exists($cakePhpVersion)) {
|
||||
if ($this->reflectionProvider->hasClass($cakePhpVersion)) {
|
||||
return $cakePhpVersion;
|
||||
}
|
||||
|
||||
|
@ -89,28 +89,15 @@ CODE_SAMPLE
|
||||
|
||||
private function shouldSkipMethodCall(MethodCall $methodCall): bool
|
||||
{
|
||||
$methodCallVar = $methodCall->var;
|
||||
|
||||
$scope = $methodCallVar->getAttribute(Scope::class);
|
||||
if ($scope === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$type = $scope->getType($methodCallVar);
|
||||
$variableType = $this->getStaticType($methodCall->var);
|
||||
|
||||
// From PropertyFetch → skip
|
||||
if ($type instanceof ThisType) {
|
||||
if ($variableType instanceof ThisType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is Boolean return → skip
|
||||
$scope = $methodCall->getAttribute(Scope::class);
|
||||
if ($scope === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$type = $scope->getType($methodCall);
|
||||
if ($type instanceof BooleanType) {
|
||||
$methodCallReturnType = $this->getStaticType($methodCall);
|
||||
if ($methodCallReturnType instanceof BooleanType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,15 @@
|
||||
|
||||
namespace Rector\CodeQualityStrict\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Fixture;
|
||||
|
||||
class SkipIfConditionMethodCallReturnBool
|
||||
{
|
||||
public function run($arg)
|
||||
{
|
||||
$obj = new self();
|
||||
use Rector\CodeQualityStrict\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Source\ClassMethodWithCall;
|
||||
|
||||
if ($obj->condition($arg)) {
|
||||
final class SkipIfConditionMethodCallReturnBool
|
||||
{
|
||||
public function run(ClassMethodWithCall $classMethodWithCall, $arg)
|
||||
{
|
||||
if ($classMethodWithCall->condition($arg)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function condition($arg): bool
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQualityStrict\Tests\Rector\If_\MoveOutMethodCallInsideIfConditionRector\Source;
|
||||
|
||||
final class ClassMethodWithCall
|
||||
{
|
||||
public function condition($arg): bool
|
||||
{
|
||||
return mt_rand(0, 1) ? true : false;
|
||||
}
|
||||
}
|
@ -8,15 +8,15 @@ use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class CallableClassMethodMatcher
|
||||
@ -31,32 +31,32 @@ final class CallableClassMethodMatcher
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var NodeRepository
|
||||
*/
|
||||
private $nodeRepository;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ValueResolver $valueResolver,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
NodeRepository $nodeRepository,
|
||||
NodeNameResolver $nodeNameResolver
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Variable|PropertyFetch $objectExpr
|
||||
*/
|
||||
public function match(Expr $objectExpr, String_ $string): ?ClassMethod
|
||||
public function match(Expr $objectExpr, String_ $string): ?PhpMethodReflection
|
||||
{
|
||||
$methodName = $this->valueResolver->getValue($string);
|
||||
if (! is_string($methodName)) {
|
||||
@ -67,25 +67,29 @@ final class CallableClassMethodMatcher
|
||||
$objectType = $this->popFirstObjectType($objectType);
|
||||
|
||||
if ($objectType instanceof ObjectType) {
|
||||
$class = $this->nodeRepository->findClass($objectType->getClassName());
|
||||
|
||||
if (! $class instanceof Class_) {
|
||||
if (! $this->reflectionProvider->hasClass($objectType->getClassName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethod = $class->getMethod($methodName);
|
||||
$classReflection = $this->reflectionProvider->getClass($objectType->getClassName());
|
||||
if (! $classReflection->hasMethod($methodName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
$stringScope = $string->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$methodReflection = $classReflection->getMethod($methodName, $stringScope);
|
||||
if (! $methodReflection instanceof PhpMethodReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->nodeNameResolver->isName($objectExpr, 'this')) {
|
||||
return $classMethod;
|
||||
return $methodReflection;
|
||||
}
|
||||
|
||||
// is public method of another service
|
||||
if ($classMethod->isPublic()) {
|
||||
return $classMethod;
|
||||
if ($methodReflection->isPublic()) {
|
||||
return $methodReflection;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,21 +11,19 @@ use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use PHPStan\Reflection\FunctionVariantWithPhpDocs;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\VoidType;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
|
||||
final class AnonymousFunctionFactory
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var NodeFactory
|
||||
*/
|
||||
@ -36,40 +34,47 @@ final class AnonymousFunctionFactory
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
NodeFactory $nodeFactory,
|
||||
NodeNameResolver $nodeNameResolver
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
StaticTypeMapper $staticTypeMapper
|
||||
) {
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Variable|PropertyFetch $node
|
||||
*/
|
||||
public function create(ClassMethod $classMethod, Node $node): Closure
|
||||
public function create(PhpMethodReflection $phpMethodReflection, Node $node): Closure
|
||||
{
|
||||
/** @var Return_[] $classMethodReturns */
|
||||
$classMethodReturns = $this->betterNodeFinder->findInstanceOf((array) $classMethod->stmts, Return_::class);
|
||||
/** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */
|
||||
$functionVariantWithPhpDoc = $phpMethodReflection->getVariants()[0];
|
||||
|
||||
$anonymousFunction = new Closure();
|
||||
$newParams = $this->copyParams($classMethod->params);
|
||||
$newParams = $this->createParams($functionVariantWithPhpDoc->getParameters());
|
||||
|
||||
$anonymousFunction->params = $newParams;
|
||||
|
||||
$innerMethodCall = new MethodCall($node, $classMethod->name);
|
||||
$innerMethodCall = new MethodCall($node, $phpMethodReflection->getName());
|
||||
$innerMethodCall->args = $this->nodeFactory->createArgsFromParams($newParams);
|
||||
|
||||
if ($classMethod->returnType !== null) {
|
||||
$newReturnType = $classMethod->returnType;
|
||||
$newReturnType->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
$anonymousFunction->returnType = $newReturnType;
|
||||
if (! $functionVariantWithPhpDoc->getReturnType() instanceof MixedType) {
|
||||
$returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
|
||||
$functionVariantWithPhpDoc->getReturnType()
|
||||
);
|
||||
$anonymousFunction->returnType = $returnType;
|
||||
}
|
||||
|
||||
// does method return something?
|
||||
if ($this->hasClassMethodReturn($classMethodReturns)) {
|
||||
|
||||
if (! $functionVariantWithPhpDoc->getReturnType() instanceof VoidType) {
|
||||
$anonymousFunction->stmts[] = new Return_($innerMethodCall);
|
||||
} else {
|
||||
$anonymousFunction->stmts[] = new Expression($innerMethodCall);
|
||||
@ -83,32 +88,23 @@ final class AnonymousFunctionFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Param[] $params
|
||||
* @param ParameterReflection[] $parameterReflections
|
||||
* @return Param[]
|
||||
*/
|
||||
private function copyParams(array $params): array
|
||||
private function createParams(array $parameterReflections): array
|
||||
{
|
||||
$newParams = [];
|
||||
foreach ($params as $param) {
|
||||
$newParam = clone $param;
|
||||
$newParam->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
$newParam->var->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
$newParams[] = $newParam;
|
||||
}
|
||||
$params = [];
|
||||
foreach ($parameterReflections as $parameterReflection) {
|
||||
$param = new Param(new Variable($parameterReflection->getName()));
|
||||
|
||||
return $newParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Return_[] $nodes
|
||||
*/
|
||||
private function hasClassMethodReturn(array $nodes): bool
|
||||
{
|
||||
foreach ($nodes as $node) {
|
||||
if ($node->expr !== null) {
|
||||
return true;
|
||||
if (! $parameterReflection->getType() instanceof MixedType) {
|
||||
$paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($parameterReflection->getType());
|
||||
$param->type = $paramType;
|
||||
}
|
||||
|
||||
$params[] = $param;
|
||||
}
|
||||
return false;
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeCollector\NodeAnalyzer\ArrayCallableMethodReferenceAnalyzer;
|
||||
use Rector\NodeCollector\ValueObject\ArrayCallable;
|
||||
@ -32,9 +33,17 @@ final class ArrayThisCallToThisMethodCallRector extends AbstractRector
|
||||
*/
|
||||
private $arrayCallableMethodReferenceAnalyzer;
|
||||
|
||||
public function __construct(ArrayCallableMethodReferenceAnalyzer $arrayCallableMethodReferenceAnalyzer)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
ArrayCallableMethodReferenceAnalyzer $arrayCallableMethodReferenceAnalyzer,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->arrayCallableMethodReferenceAnalyzer = $arrayCallableMethodReferenceAnalyzer;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
@ -94,6 +103,7 @@ CODE_SAMPLE
|
||||
if (! $arrayCallable instanceof ArrayCallable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isAssignedToNetteMagicOnProperty($node)) {
|
||||
return null;
|
||||
}
|
||||
@ -107,12 +117,18 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $arrayCallable->isExistingMethod()) {
|
||||
if (! $this->reflectionProvider->hasClass($arrayCallable->getClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reflectionMethod = $arrayCallable->getReflectionMethod();
|
||||
$classReflection = $this->reflectionProvider->getClass($arrayCallable->getClass());
|
||||
if (! $classReflection->hasMethod($arrayCallable->getMethod())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nativeReflectionClass = $classReflection->getNativeReflection();
|
||||
|
||||
$reflectionMethod = $nativeReflectionClass->getMethod($arrayCallable->getMethod());
|
||||
$this->privatizeClassMethod($reflectionMethod);
|
||||
|
||||
if ($reflectionMethod->getNumberOfParameters() > 0) {
|
||||
|
@ -11,9 +11,8 @@ use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use Rector\CodeQuality\NodeAnalyzer\CallableClassMethodMatcher;
|
||||
use Rector\CodeQuality\NodeFactory\AnonymousFunctionFactory;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
@ -133,12 +132,12 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethod = $this->callableClassMethodMatcher->match($objectVariable, $methodName);
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
$phpMethodReflection = $this->callableClassMethodMatcher->match($objectVariable, $methodName);
|
||||
if (! $phpMethodReflection instanceof PhpMethodReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->anonymousFunctionFactory->create($classMethod, $objectVariable);
|
||||
return $this->anonymousFunctionFactory->create($phpMethodReflection, $objectVariable);
|
||||
}
|
||||
|
||||
private function shouldSkipArray(Array_ $array): bool
|
||||
|
@ -6,6 +6,8 @@ namespace Rector\CodeQuality\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\CodeQuality\NodeAnalyzer\ClassLikeAnalyzer;
|
||||
use Rector\CodeQuality\NodeAnalyzer\LocalPropertyAnalyzer;
|
||||
@ -38,14 +40,21 @@ final class CompleteDynamicPropertiesRector extends AbstractRector
|
||||
*/
|
||||
private $classLikeAnalyzer;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
MissingPropertiesFactory $missingPropertiesFactory,
|
||||
LocalPropertyAnalyzer $localPropertyAnalyzer,
|
||||
ClassLikeAnalyzer $classLikeAnalyzer
|
||||
ClassLikeAnalyzer $classLikeAnalyzer,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->missingPropertiesFactory = $missingPropertiesFactory;
|
||||
$this->localPropertyAnalyzer = $localPropertyAnalyzer;
|
||||
$this->classLikeAnalyzer = $classLikeAnalyzer;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
@ -96,6 +105,17 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
$className = $this->getName($node);
|
||||
if ($className === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
|
||||
// special case for Laravel Collection macro magic
|
||||
$fetchedLocalPropertyNameToTypes = $this->localPropertyAnalyzer->resolveFetchedPropertiesToTypesFromClass(
|
||||
$node
|
||||
@ -106,10 +126,7 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $className */
|
||||
$className = $this->getName($node);
|
||||
|
||||
$propertiesToComplete = $this->filterOutExistingProperties($className, $propertiesToComplete);
|
||||
$propertiesToComplete = $this->filterOutExistingProperties($classReflection, $propertiesToComplete);
|
||||
|
||||
$newProperties = $this->missingPropertiesFactory->create(
|
||||
$fetchedLocalPropertyNameToTypes,
|
||||
@ -127,17 +144,23 @@ CODE_SAMPLE
|
||||
return true;
|
||||
}
|
||||
|
||||
$className = $this->getName($class);
|
||||
$className = $this->nodeNameResolver->getName($class);
|
||||
if ($className === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// properties are accessed via magic, nothing we can do
|
||||
if (method_exists($className, '__set')) {
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return method_exists($className, '__get');
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
|
||||
// properties are accessed via magic, nothing we can do
|
||||
if ($classReflection->hasMethod('__set')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $classReflection->hasMethod('__get');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,14 +181,13 @@ CODE_SAMPLE
|
||||
* @param string[] $propertiesToComplete
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterOutExistingProperties(string $className, array $propertiesToComplete): array
|
||||
private function filterOutExistingProperties(ClassReflection $classReflection, array $propertiesToComplete): array
|
||||
{
|
||||
$missingPropertyNames = [];
|
||||
|
||||
// remove other properties that are accessible from this scope
|
||||
foreach ($propertiesToComplete as $propertyToComplete) {
|
||||
/** @var string $propertyToComplete */
|
||||
if (property_exists($className, $propertyToComplete)) {
|
||||
if ($classReflection->hasProperty($propertyToComplete)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ CODE_SAMPLE
|
||||
}
|
||||
|
||||
$valueArgument = $node->args[1]->value;
|
||||
if (! $this->isStaticType($valueArgument, StringType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($valueArgument, StringType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -78,11 +78,11 @@ CODE_SAMPLE
|
||||
|
||||
if ($node->expr instanceof Identical) {
|
||||
$identical = $node->expr;
|
||||
if (! $this->isStaticType($identical->left, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isStaticType($identical->right, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -94,11 +94,11 @@ CODE_SAMPLE
|
||||
|
||||
private function processIdentical(Identical $identical): ?NotIdentical
|
||||
{
|
||||
if (! $this->isStaticType($identical->left, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isStaticType($identical->right, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -184,9 +184,9 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
$tagValueNode = $phpDocInfo->getVarTagValueNode();
|
||||
if ($tagValueNode instanceof VarTagValueNode) {
|
||||
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $tagValueNode);
|
||||
$varTagValueNode = $phpDocInfo->getVarTagValueNode();
|
||||
if ($varTagValueNode instanceof VarTagValueNode) {
|
||||
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $varTagValueNode);
|
||||
}
|
||||
|
||||
return new BooleanNot(new Instanceof_($expr, new FullyQualified($type->getClassName())));
|
||||
|
@ -64,12 +64,13 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($this->isStaticType($node->left, BooleanType::class) && ! $this->valueResolver->isTrueOrFalse(
|
||||
$node->left
|
||||
)) {
|
||||
if ($this->nodeTypeResolver->isStaticType(
|
||||
$node->left,
|
||||
BooleanType::class
|
||||
) && ! $this->valueResolver->isTrueOrFalse($node->left)) {
|
||||
return $this->processBoolTypeToNotBool($node, $node->left, $node->right);
|
||||
}
|
||||
if (! $this->isStaticType($node->right, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($node->right, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
if ($this->valueResolver->isTrueOrFalse($node->right)) {
|
||||
|
@ -144,11 +144,11 @@ CODE_SAMPLE
|
||||
return $this->resolveString($isNegated, $expr);
|
||||
}
|
||||
|
||||
if ($this->isStaticType($expr, IntegerType::class)) {
|
||||
if ($this->nodeTypeResolver->isStaticType($expr, IntegerType::class)) {
|
||||
return $this->resolveInteger($isNegated, $expr);
|
||||
}
|
||||
|
||||
if ($this->isStaticType($expr, FloatType::class)) {
|
||||
if ($this->nodeTypeResolver->isStaticType($expr, FloatType::class)) {
|
||||
return $this->resolveFloat($isNegated, $expr);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ use PhpParser\Node\Expr\Isset_;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
@ -24,6 +25,16 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
*/
|
||||
final class IssetOnPropertyObjectToPropertyExistsRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change isset on property object to property_exists() and not null check', [
|
||||
@ -86,15 +97,28 @@ CODE_SAMPLE
|
||||
}
|
||||
|
||||
$propertyFetchVarType = $this->getObjectType($issetVar->var);
|
||||
if ($propertyFetchVarType instanceof TypeWithClassName && property_exists(
|
||||
$propertyFetchVarType->getClassName(),
|
||||
$propertyFetchName
|
||||
)) {
|
||||
$newNodes[] = $this->createNotIdenticalToNull($issetVar);
|
||||
continue;
|
||||
}
|
||||
if ($propertyFetchVarType instanceof TypeWithClassName) {
|
||||
if (! $this->reflectionProvider->hasClass($propertyFetchVarType->getClassName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newNodes[] = $this->replaceToPropertyExistsWithNullCheck($issetVar->var, $propertyFetchName, $issetVar);
|
||||
$classReflection = $this->reflectionProvider->getClass($propertyFetchVarType->getClassName());
|
||||
if (! $classReflection->hasProperty($propertyFetchName)) {
|
||||
$newNodes[] = $this->replaceToPropertyExistsWithNullCheck(
|
||||
$issetVar->var,
|
||||
$propertyFetchName,
|
||||
$issetVar
|
||||
);
|
||||
} else {
|
||||
$newNodes[] = $this->createNotIdenticalToNull($issetVar);
|
||||
}
|
||||
} else {
|
||||
$newNodes[] = $this->replaceToPropertyExistsWithNullCheck(
|
||||
$issetVar->var,
|
||||
$propertyFetchName,
|
||||
$issetVar
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->nodeFactory->createReturnBooleanAnd($newNodes);
|
||||
|
@ -61,7 +61,7 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isStaticType($node->cond, BooleanType::class)) {
|
||||
if (! $this->nodeTypeResolver->isStaticType($node->cond, BooleanType::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ final class UnnecessaryTernaryExpressionRector extends AbstractRector
|
||||
private function processNonBinaryCondition(Expr $ifExpression, Expr $elseExpression, Expr $condition): ?Node
|
||||
{
|
||||
if ($this->valueResolver->isTrue($ifExpression) && $this->valueResolver->isFalse($elseExpression)) {
|
||||
if ($this->isStaticType($condition, BooleanType::class)) {
|
||||
if ($this->nodeTypeResolver->isStaticType($condition, BooleanType::class)) {
|
||||
return $condition;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ final class UnnecessaryTernaryExpressionRector extends AbstractRector
|
||||
}
|
||||
|
||||
if ($this->valueResolver->isFalse($ifExpression) && $this->valueResolver->isTrue($elseExpression)) {
|
||||
if ($this->isStaticType($condition, BooleanType::class)) {
|
||||
if ($this->nodeTypeResolver->isStaticType($condition, BooleanType::class)) {
|
||||
return new BooleanNot($condition);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;
|
||||
|
||||
use Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Source\SortingClass;
|
||||
|
||||
class AnotherClass
|
||||
{
|
||||
/**
|
||||
@ -24,30 +26,14 @@ class AnotherClass
|
||||
}
|
||||
}
|
||||
|
||||
final class SortingClass
|
||||
{
|
||||
public function publicSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
protected function protectedSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
private function privateSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;
|
||||
|
||||
use Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Source\SortingClass;
|
||||
|
||||
class AnotherClass
|
||||
{
|
||||
/**
|
||||
@ -74,22 +60,4 @@ class AnotherClass
|
||||
}
|
||||
}
|
||||
|
||||
final class SortingClass
|
||||
{
|
||||
public function publicSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
protected function protectedSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
private function privateSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Source;
|
||||
|
||||
final class SortingClass
|
||||
{
|
||||
public function publicSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
protected function protectedSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
|
||||
private function privateSort($a, $b)
|
||||
{
|
||||
return $a <=> $b;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\Fixture;
|
||||
|
||||
class PropertyAfterInstantiationIfAlwaysExists
|
||||
final class PropertyAfterInstantiationIfAlwaysExists
|
||||
{
|
||||
public $x;
|
||||
public $y;
|
||||
@ -22,7 +22,7 @@ class PropertyAfterInstantiationIfAlwaysExists
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\Fixture;
|
||||
|
||||
class PropertyAfterInstantiationIfAlwaysExists
|
||||
final class PropertyAfterInstantiationIfAlwaysExists
|
||||
{
|
||||
public $x;
|
||||
public $y;
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\Fixture;
|
||||
|
||||
class PropertyMaybeExistsAfterInstantiation
|
||||
final class PropertyMaybeExistsAfterInstantiation
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->x = 'a';
|
||||
$this->y = 'b';
|
||||
}
|
||||
|
||||
public function run()
|
||||
@ -16,8 +15,6 @@ class PropertyMaybeExistsAfterInstantiation
|
||||
$obj->init();
|
||||
|
||||
isset($obj->x);
|
||||
isset($obj->y);
|
||||
isset($obj->x) && isset($obj->y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,12 +24,11 @@ class PropertyMaybeExistsAfterInstantiation
|
||||
|
||||
namespace Rector\CodeQuality\Tests\Rector\Isset_\IssetOnPropertyObjectToPropertyExistsRector\Fixture;
|
||||
|
||||
class PropertyMaybeExistsAfterInstantiation
|
||||
final class PropertyMaybeExistsAfterInstantiation
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->x = 'a';
|
||||
$this->y = 'b';
|
||||
}
|
||||
|
||||
public function run()
|
||||
@ -41,8 +37,6 @@ class PropertyMaybeExistsAfterInstantiation
|
||||
$obj->init();
|
||||
|
||||
property_exists($obj, 'x') && $obj->x !== null;
|
||||
property_exists($obj, 'y') && $obj->y !== null;
|
||||
property_exists($obj, 'x') && $obj->x !== null && (property_exists($obj, 'y') && $obj->y !== null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,11 @@ use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\CodingStyle\ClassNameImport\AliasUsesResolver;
|
||||
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PostRector\Collector\UseNodesToAddCollector;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
@ -66,6 +66,11 @@ final class NameImporter
|
||||
*/
|
||||
private $renamedClassesCollector;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(
|
||||
AliasUsesResolver $aliasUsesResolver,
|
||||
ClassNameImportSkipper $classNameImportSkipper,
|
||||
@ -73,7 +78,8 @@ final class NameImporter
|
||||
ParameterProvider $parameterProvider,
|
||||
RenamedClassesCollector $renamedClassesCollector,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
UseNodesToAddCollector $useNodesToAddCollector
|
||||
UseNodesToAddCollector $useNodesToAddCollector,
|
||||
ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->aliasUsesResolver = $aliasUsesResolver;
|
||||
@ -82,6 +88,7 @@ final class NameImporter
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
$this->useNodesToAddCollector = $useNodesToAddCollector;
|
||||
$this->renamedClassesCollector = $renamedClassesCollector;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function importName(Name $name): ?Name
|
||||
@ -198,15 +205,17 @@ final class NameImporter
|
||||
if ($autoImportNames && ! $parentNode instanceof Node && ! Strings::contains(
|
||||
$fullName,
|
||||
'\\'
|
||||
) && function_exists($fullName)) {
|
||||
) && $this->reflectionProvider->hasFunction(new Name($fullName), null)) {
|
||||
return true;
|
||||
}
|
||||
if ($parentNode instanceof ConstFetch) {
|
||||
return count($name->parts) === 1;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof FuncCall) {
|
||||
return count($name->parts) === 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -225,11 +234,12 @@ final class NameImporter
|
||||
}
|
||||
|
||||
// skip-non existing class-likes and functions
|
||||
if (ClassExistenceStaticHelper::doesClassLikeExist($classOrFunctionName)) {
|
||||
if ($this->reflectionProvider->hasClass($classOrFunctionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! function_exists($classOrFunctionName);
|
||||
$parent = $name->getAttribute(AttributeKey::PARENT_NODE);
|
||||
return ! $parent instanceof FuncCall;
|
||||
}
|
||||
|
||||
private function addUseImport(Name $name, FullyQualifiedObjectType $fullyQualifiedObjectType): void
|
||||
|
@ -92,12 +92,14 @@ CODE_SAMPLE
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->getName($node->name);
|
||||
|
||||
foreach ($classReflection->getParentClassesNames() as $parentClassName) {
|
||||
if (! method_exists($parentClassName, $methodName)) {
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if (! $parentClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentReflectionMethod = new ReflectionMethod($parentClassName, $methodName);
|
||||
$nativeClassReflection = $parentClassReflection->getNativeReflection();
|
||||
|
||||
$parentReflectionMethod = $nativeClassReflection->getMethod($methodName);
|
||||
if ($this->isClassMethodCompatibleWithParentReflectionMethod($node, $parentReflectionMethod)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ final class YieldClassMethodToArrayClassMethodRector extends AbstractRector impl
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const METHODS_BY_TYPE = '$methodsByType';
|
||||
public const METHODS_BY_TYPE = 'methods_by_type';
|
||||
|
||||
/**
|
||||
* @var string[][]
|
||||
* @var array<class-string, string[]>
|
||||
*/
|
||||
private $methodsByType = [];
|
||||
|
||||
@ -41,7 +41,7 @@ final class YieldClassMethodToArrayClassMethodRector extends AbstractRector impl
|
||||
private $nodeTransformer;
|
||||
|
||||
/**
|
||||
* @param string[][] $methodsByType
|
||||
* @param array<class-string, string[]> $methodsByType
|
||||
*/
|
||||
public function __construct(NodeTransformer $nodeTransformer, array $methodsByType = [])
|
||||
{
|
||||
|
@ -49,7 +49,7 @@ final class PreferThisOrSelfMethodCallRector extends AbstractRector implements C
|
||||
private const SELF = 'self';
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
* @var array<class-string, string>
|
||||
*/
|
||||
private $typeToPreference = [];
|
||||
|
||||
@ -79,7 +79,7 @@ CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
self::TYPE_TO_PREFERENCE => [
|
||||
'\PHPUnit\Framework\TestCase' => self::PREFER_SELF,
|
||||
'PHPUnit\Framework\TestCase' => self::PREFER_SELF,
|
||||
],
|
||||
]
|
||||
),
|
||||
|
@ -9,6 +9,7 @@ use PhpParser\Node\Expr\BinaryOp\Concat;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
@ -18,6 +19,16 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
*/
|
||||
final class SplitStringClassConstantToClassConstFetchRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition(
|
||||
@ -78,7 +89,7 @@ CODE_SAMPLE
|
||||
// a possible constant reference
|
||||
[$possibleClass, $secondPart] = explode('::', $node->value);
|
||||
|
||||
if (! class_exists($possibleClass)) {
|
||||
if (! $this->reflectionProvider->hasClass($possibleClass)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
@ -24,6 +25,16 @@ final class UseClassKeywordForClassNameResolutionRector extends AbstractRector
|
||||
*/
|
||||
private const CLASS_BEFORE_STATIC_ACCESS_REGEX = '#(?<class_name>[\\\\a-zA-Z0-9_\\x80-\\xff]*)::#';
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition(
|
||||
@ -83,7 +94,7 @@ CODE_SAMPLE
|
||||
$classNames = [];
|
||||
|
||||
foreach ($matches['class_name'] as $matchedClassName) {
|
||||
if (! class_exists($matchedClassName)) {
|
||||
if (! $this->reflectionProvider->hasClass($matchedClassName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -119,7 +130,7 @@ CODE_SAMPLE
|
||||
{
|
||||
$exprsToConcat = [];
|
||||
foreach ($parts as $part) {
|
||||
if (class_exists($part)) {
|
||||
if ($this->reflectionProvider->hasClass($part)) {
|
||||
$exprsToConcat[] = new ClassConstFetch(new FullyQualified(ltrim($part, '\\')), 'class');
|
||||
} else {
|
||||
$exprsToConcat[] = new String_($part);
|
||||
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\ClassConst\VarConstantCommentRector\Fixture;
|
||||
|
||||
class SkipClassString
|
||||
{
|
||||
/**
|
||||
* @var class-string
|
||||
*/
|
||||
const HI = self::class;
|
||||
}
|
@ -2,15 +2,14 @@
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector\Fixture;
|
||||
|
||||
class FixtureFromMethodCall
|
||||
final class FixtureFromMethodCall
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$self = new self();
|
||||
count($self->getData()) === 0;
|
||||
0 === count($self->getData());
|
||||
count($self->getData()) > 0;
|
||||
0 < count($self->getData());
|
||||
count($this->getData()) === 0;
|
||||
0 === count($this->getData());
|
||||
count($this->getData()) > 0;
|
||||
0 < count($this->getData());
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
@ -25,15 +24,14 @@ class FixtureFromMethodCall
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector\Fixture;
|
||||
|
||||
class FixtureFromMethodCall
|
||||
final class FixtureFromMethodCall
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$self = new self();
|
||||
$self->getData() === [];
|
||||
[] === $self->getData();
|
||||
$self->getData() !== [];
|
||||
[] !== $self->getData();
|
||||
$this->getData() === [];
|
||||
[] === $this->getData();
|
||||
$this->getData() !== [];
|
||||
[] !== $this->getData();
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
|
@ -23,16 +23,25 @@ final class ImportFullyQualifiedNamesRectorTest extends AbstractRectorTestCase
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideDataFunction(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureFunction');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideDataGeneric(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/FixtureGeneric');
|
||||
|
@ -9,12 +9,12 @@ use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
||||
use Rector\DeadCode\Comparator\Parameter\ParameterDefaultsComparator;
|
||||
use Rector\DeadCode\Comparator\Parameter\ParameterTypeComparator;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeCollector\Reflection\MethodReflectionProvider;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
@ -31,11 +31,6 @@ final class CurrentAndParentClassMethodComparator
|
||||
*/
|
||||
private $nodeComparator;
|
||||
|
||||
/**
|
||||
* @var NodeRepository
|
||||
*/
|
||||
private $nodeRepository;
|
||||
|
||||
/**
|
||||
* @var MethodReflectionProvider
|
||||
*/
|
||||
@ -53,14 +48,12 @@ final class CurrentAndParentClassMethodComparator
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
NodeRepository $nodeRepository,
|
||||
MethodReflectionProvider $methodReflectionProvider,
|
||||
ParameterDefaultsComparator $parameterDefaultsComparator,
|
||||
ParameterTypeComparator $parameterTypeComparator,
|
||||
NodeComparator $nodeComparator
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->methodReflectionProvider = $methodReflectionProvider;
|
||||
$this->parameterDefaultsComparator = $parameterDefaultsComparator;
|
||||
$this->parameterTypeComparator = $parameterTypeComparator;
|
||||
@ -126,46 +119,54 @@ final class CurrentAndParentClassMethodComparator
|
||||
ClassMethod $classMethod,
|
||||
StaticCall $staticCall
|
||||
): bool {
|
||||
/** @var string $className */
|
||||
$className = $staticCall->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
$parentClassName = get_parent_class($className);
|
||||
if (! $parentClassName) {
|
||||
throw new ShouldNotHappenException();
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($staticCall->name);
|
||||
if ($methodName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parentClassMethod = $this->nodeRepository->findClassMethod($parentClassName, $methodName);
|
||||
if (! $parentClassMethod instanceof ClassMethod) {
|
||||
return $this->checkOverrideUsingReflection($classMethod, $parentClassName, $methodName);
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
if (! $parentClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nativeParentClassReflection = $parentClassReflection->getNativeReflection();
|
||||
$nativeParentClassMethodReflection = $nativeParentClassReflection->getMethod($methodName);
|
||||
|
||||
if (! $nativeParentClassMethodReflection->isProtected()) {
|
||||
return $this->checkOverrideUsingReflection($classMethod, $parentClassReflection, $methodName);
|
||||
}
|
||||
|
||||
if (! $nativeParentClassMethodReflection->isPublic()) {
|
||||
return $this->checkOverrideUsingReflection($classMethod, $parentClassReflection, $methodName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (! $parentClassMethod->isProtected()) {
|
||||
return $this->checkOverrideUsingReflection($classMethod, $parentClassName, $methodName);
|
||||
}
|
||||
if (! $classMethod->isPublic()) {
|
||||
return $this->checkOverrideUsingReflection($classMethod, $parentClassName, $methodName);
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkOverrideUsingReflection(
|
||||
ClassMethod $classMethod,
|
||||
string $parentClassName,
|
||||
ClassReflection $classReflection,
|
||||
string $methodName
|
||||
): bool {
|
||||
// @todo use phpstan reflecoitn
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$parentMethodReflection = $this->methodReflectionProvider->provideByClassAndMethodName(
|
||||
$parentClassName,
|
||||
$methodName,
|
||||
$scope
|
||||
);
|
||||
$parentMethodReflection = $classReflection->getMethod($methodName, $scope);
|
||||
|
||||
// 3rd party code
|
||||
if ($parentMethodReflection !== null) {
|
||||
|
@ -11,12 +11,15 @@ use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use ReflectionFunction;
|
||||
use ReflectionParameter;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
|
||||
final class CallDefaultParamValuesResolver
|
||||
{
|
||||
@ -30,10 +33,26 @@ final class CallDefaultParamValuesResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(NodeRepository $nodeRepository, NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var PrivatesAccessor
|
||||
*/
|
||||
private $privatesAccessor;
|
||||
|
||||
public function __construct(
|
||||
NodeRepository $nodeRepository,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
PrivatesAccessor $privatesAccessor
|
||||
) {
|
||||
$this->nodeRepository = $nodeRepository;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->privatesAccessor = $privatesAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,29 +108,39 @@ final class CallDefaultParamValuesResolver
|
||||
*/
|
||||
private function resolveFromFunctionName(string $functionName): array
|
||||
{
|
||||
$functionNode = $this->nodeRepository->findFunction($functionName);
|
||||
if ($functionNode !== null) {
|
||||
return $this->resolveFromFunctionLike($functionNode);
|
||||
$function = $this->nodeRepository->findFunction($functionName);
|
||||
if ($function instanceof Function_) {
|
||||
return $this->resolveFromFunctionLike($function);
|
||||
}
|
||||
|
||||
// non existing function
|
||||
if (! function_exists($functionName)) {
|
||||
$functionNameNode = new Name($functionName);
|
||||
if (! $this->reflectionProvider->hasFunction($functionNameNode, null)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$reflectionFunction = new ReflectionFunction($functionName);
|
||||
if ($reflectionFunction->isUserDefined()) {
|
||||
$defaultValues = [];
|
||||
|
||||
foreach ($reflectionFunction->getParameters() as $key => $reflectionParameter) {
|
||||
if ($reflectionParameter->isDefaultValueAvailable()) {
|
||||
$defaultValues[$key] = BuilderHelpers::normalizeValue($reflectionParameter->getDefaultValue());
|
||||
}
|
||||
}
|
||||
|
||||
return $defaultValues;
|
||||
$functionReflection = $this->reflectionProvider->getFunction($functionNameNode, null);
|
||||
if ($functionReflection->isBuiltin()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [];
|
||||
$defaultValues = [];
|
||||
|
||||
$parametersAcceptor = $functionReflection->getVariants()[0];
|
||||
|
||||
foreach ($parametersAcceptor->getParameters() as $key => $reflectionParameter) {
|
||||
/** @var ReflectionParameter $nativeReflectionParameter */
|
||||
$nativeReflectionParameter = $this->privatesAccessor->getPrivateProperty(
|
||||
$reflectionParameter,
|
||||
'reflection'
|
||||
);
|
||||
if (! $nativeReflectionParameter->isDefaultValueAvailable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$defaultValues[$key] = BuilderHelpers::normalizeValue($nativeReflectionParameter->getDefaultValue());
|
||||
}
|
||||
|
||||
return $defaultValues;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ namespace Rector\DeadCode\Rector\ClassConst;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\ApiPhpDocTagNode;
|
||||
use Rector\Caching\Contract\Rector\ZeroCacheRectorInterface;
|
||||
use Rector\Core\NodeManipulator\ClassConstManipulator;
|
||||
@ -74,12 +77,15 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string|null $class */
|
||||
$class = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($class === null) {
|
||||
return null;
|
||||
/** @var Scope $scope */
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
$nodeRepositoryFindInterface = $this->nodeRepository->findInterface($class);
|
||||
|
||||
$nodeRepositoryFindInterface = $this->nodeRepository->findInterface($classReflection->getName());
|
||||
|
||||
// 0. constants declared in interfaces have to be public
|
||||
if ($nodeRepositoryFindInterface !== null) {
|
||||
@ -90,13 +96,18 @@ CODE_SAMPLE
|
||||
/** @var string $constant */
|
||||
$constant = $this->getName($node);
|
||||
|
||||
$directUseClasses = $this->nodeRepository->findDirectClassConstantFetches($class, $constant);
|
||||
if ($directUseClasses !== []) {
|
||||
return null;
|
||||
}
|
||||
$directUsingClassReflections = $this->nodeRepository->findDirectClassConstantFetches(
|
||||
$classReflection,
|
||||
$constant
|
||||
);
|
||||
|
||||
$indirectUseClasses = $this->nodeRepository->findIndirectClassConstantFetches($class, $constant);
|
||||
if ($indirectUseClasses !== []) {
|
||||
$indirectUsingClassReflections = $this->nodeRepository->findIndirectClassConstantFetches(
|
||||
$classReflection,
|
||||
$constant
|
||||
);
|
||||
|
||||
$usingClassReflections = array_merge($directUsingClassReflections, $indirectUsingClassReflections);
|
||||
if ($usingClassReflections !== []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -125,6 +136,7 @@ CODE_SAMPLE
|
||||
}
|
||||
|
||||
$classLike = $classConst->getAttribute(AttributeKey::CLASS_NODE);
|
||||
|
||||
if ($classLike instanceof ClassLike) {
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classLike);
|
||||
return $phpDocInfo->hasByType(ApiPhpDocTagNode::class);
|
||||
|
@ -111,7 +111,7 @@ CODE_SAMPLE
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->isName($classMethod, '__*')) {
|
||||
if ($classMethod->isMagic()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user