make PHPStan service part of Rector container

This commit is contained in:
Tomas Votruba 2018-08-06 12:31:17 +02:00
parent 3bcc6e5e9d
commit ac1ca7dfda
16 changed files with 190 additions and 123 deletions

View File

@ -0,0 +1,44 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\DependencyInjection;
use Nette\DI\Container;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\ScopeFactory;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Broker\Broker;
use PHPStan\DependencyInjection\ContainerFactory;
final class PHPStanServicesFactory
{
/**
* @var Container
*/
private $container;
public function __construct()
{
$this->container = (new ContainerFactory(getcwd()))
->create(sys_get_temp_dir(), []);
}
public function createBroker(): Broker
{
return $this->container->getByType(Broker::class);
}
public function createNodeScopeResolver(): NodeScopeResolver
{
return $this->container->getByType(NodeScopeResolver::class);
}
public function createTypeSpecifier(): TypeSpecifier
{
return $this->container->getByType(TypeSpecifier::class);
}
public function createScopeFactory(): ScopeFactory
{
return $this->container->getByType(ScopeFactory::class);
}
}

View File

@ -20,7 +20,6 @@ use Rector\Node\Attribute;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
use Rector\Php\TypeAnalyzer;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
final class NodeTypeResolver
{
@ -44,14 +43,21 @@ final class NodeTypeResolver
*/
private $typeAnalyzer;
/**
* @var Broker
*/
private $broker;
public function __construct(
ClassReflectionTypesResolver $classReflectionTypesResolver,
DocBlockAnalyzer $docBlockAnalyzer,
TypeAnalyzer $typeAnalyzer
TypeAnalyzer $typeAnalyzer,
Broker $broker
) {
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
$this->docBlockAnalyzer = $docBlockAnalyzer;
$this->typeAnalyzer = $typeAnalyzer;
$this->broker = $broker;
}
public function addPerNodeTypeResolver(PerNodeTypeResolverInterface $perNodeTypeResolver): void
@ -94,10 +100,8 @@ final class NodeTypeResolver
$propertyTypes = $this->filterOutScalarTypes($propertyTypes);
/** @var Broker $broker */
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
foreach ($propertyTypes as $propertyType) {
$propertyClassReflection = $broker->getClass($propertyType);
$propertyClassReflection = $this->broker->getClass($propertyType);
$propertyTypes += $this->classReflectionTypesResolver->resolve($propertyClassReflection);
}
@ -174,9 +178,8 @@ final class NodeTypeResolver
$types = $this->resolveObjectTypesToStrings($type);
// complete parents
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
foreach ($types as $type) {
$propertyClassReflection = $broker->getClass($type);
$propertyClassReflection = $this->broker->getClass($type);
$types = array_merge($types, $this->classReflectionTypesResolver->resolve($propertyClassReflection));
}
@ -186,13 +189,12 @@ final class NodeTypeResolver
// get from annotation
$variableTypes = $this->docBlockAnalyzer->getVarTypes($variableNode);
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
foreach ($variableTypes as $i => $type) {
if (! class_exists($type)) {
unset($variableTypes[$i]);
continue;
}
$propertyClassReflection = $broker->getClass($type);
$propertyClassReflection = $this->broker->getClass($type);
$variableTypes = array_merge(
$variableTypes,
$this->classReflectionTypesResolver->resolve($propertyClassReflection)

View File

@ -0,0 +1,57 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\PHPStan\Scope;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\ScopeContext;
use PHPStan\Analyser\ScopeFactory as PHPStanScopeFactory;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Broker\Broker;
use Rector\Printer\BetterStandardPrinter;
use Symfony\Component\Finder\SplFileInfo;
final class ScopeFactory
{
/**
* @var Broker
*/
private $broker;
/**
* @var TypeSpecifier
*/
private $typeSpecifier;
/**
* @var PHPStanScopeFactory
*/
private $pHPStanScopeFactory;
/**
* @var BetterStandardPrinter
*/
private $betterStandardPrinter;
public function __construct(
Broker $broker,
TypeSpecifier $typeSpecifier,
PHPStanScopeFactory $pHPStanScopeFactory,
BetterStandardPrinter $betterStandardPrinter
) {
$this->broker = $broker;
$this->typeSpecifier = $typeSpecifier;
$this->pHPStanScopeFactory = $pHPStanScopeFactory;
$this->betterStandardPrinter = $betterStandardPrinter;
}
public function createFromFileInfo(SplFileInfo $splFileInfo): Scope
{
return new Scope(
$this->pHPStanScopeFactory,
$this->broker,
$this->betterStandardPrinter,
$this->typeSpecifier,
ScopeContext::create($splFileInfo->getRealPath())
);
}
}

View File

@ -8,18 +8,13 @@ use PhpParser\Node\Stmt\Interface_;
use PhpParser\NodeVisitor\NameResolver;
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\ScopeContext;
use PHPStan\Analyser\ScopeFactory;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\Broker\Broker;
use PHPStan\DependencyInjection\ContainerFactory;
use Rector\Configuration\Option;
use Rector\Exception\ShouldNotHappenException;
use Rector\FileSystem\FilesFinder;
use Rector\Node\Attribute;
use Rector\NodeTypeResolver\Configuration\CurrentFileProvider;
use Rector\Printer\BetterStandardPrinter;
use Symfony\Component\Finder\SplFileInfo;
use Rector\NodeTypeResolver\PHPStan\Scope\ScopeFactory;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
@ -49,44 +44,30 @@ final class PHPStanNodeScopeResolver
*/
private $filesFinder;
/**
* @var Broker
*/
private $phpstanBroker;
/**
* @var TypeSpecifier
*/
private $phpstanTypeSpecifier;
/**
* @var ScopeFactory
*/
private $phpstanScopeFactory;
private $scopeFactory;
/**
* @var BetterStandardPrinter
* @var Broker
*/
private $betterStandardPrinter;
private $broker;
public function __construct(
CurrentFileProvider $currentFileProvider,
ParameterProvider $parameterProvider,
BetterStandardPrinter $betterStandardPrinter,
FilesFinder $filesFinder
FilesFinder $filesFinder,
ScopeFactory $scopeFactory,
NodeScopeResolver $nodeScopeResolver,
Broker $broker
) {
$phpstanContainer = (new ContainerFactory(getcwd()))->create(sys_get_temp_dir(), []);
$this->phpstanBroker = $phpstanContainer->getByType(Broker::class);
$this->nodeScopeResolver = $phpstanContainer->getByType(NodeScopeResolver::class);
$this->phpstanTypeSpecifier = $phpstanContainer->getByType(TypeSpecifier::class);
$this->phpstanScopeFactory = $phpstanContainer->getByType(ScopeFactory::class);
$this->currentFileProvider = $currentFileProvider;
$this->parameterProvider = $parameterProvider;
$this->filesFinder = $filesFinder;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->scopeFactory = $scopeFactory;
$this->nodeScopeResolver = $nodeScopeResolver;
$this->broker = $broker;
}
/**
@ -100,13 +81,13 @@ final class PHPStanNodeScopeResolver
$this->nodeScopeResolver->processNodes(
$nodes,
$this->createScopeByFile($this->currentFileProvider->getSplFileInfo()),
$this->scopeFactory->createFromFileInfo($this->currentFileProvider->getSplFileInfo()),
function (Node $node, Scope $scope): void {
// the class reflection is resolved AFTER entering to class node
// so we need to get it from the first after this one
if ($node instanceof Class_ || $node instanceof Interface_) {
if (isset($node->namespacedName)) {
$scope = $scope->enterClass($this->phpstanBroker->getClass((string) $node->namespacedName));
$scope = $scope->enterClass($this->broker->getClass((string) $node->namespacedName));
} else {
// possibly anonymous class
$anonymousClassReflection = (new PrivatesAccessor())->getPrivateProperty(
@ -127,20 +108,6 @@ final class PHPStanNodeScopeResolver
return $nodes;
}
private function createScopeByFile(SplFileInfo $splFileInfo): Scope
{
$scopeContext = ScopeContext::create($splFileInfo->getRealPath());
// Reset scope for new file
return new Scope(
$this->phpstanScopeFactory,
$this->phpstanBroker,
$this->betterStandardPrinter,
$this->phpstanTypeSpecifier,
$scopeContext
);
}
private function setAnalysedFiles(): void
{
$source = $this->parameterProvider->provideParameter(Option::SOURCE);

View File

@ -2,25 +2,30 @@
namespace Rector\NodeTypeResolver\PerNodeTypeResolver;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Namespace_;
use Rector\BetterReflection\Reflector\SmartClassReflector;
use PHPStan\Broker\Broker;
use Rector\Node\Attribute;
use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface;
use Rector\NodeTypeResolver\Reflection\ClassReflectionTypesResolver;
final class NameTypeResolver implements PerNodeTypeResolverInterface
{
/**
* @var SmartClassReflector
* @var Broker
*/
private $smartClassReflector;
private $broker;
public function __construct(SmartClassReflector $smartClassReflector)
/**
* @var ClassReflectionTypesResolver
*/
private $classReflectionTypesResolver;
public function __construct(Broker $broker, ClassReflectionTypesResolver $classReflectionTypesResolver)
{
$this->smartClassReflector = $smartClassReflector;
$this->broker = $broker;
$this->classReflectionTypesResolver = $classReflectionTypesResolver;
}
/**
@ -41,53 +46,11 @@ final class NameTypeResolver implements PerNodeTypeResolverInterface
return [$nameNode->getAttribute(Attribute::PARENT_CLASS_NAME)];
}
if ($this->shouldSkip($nameNode, $nameNode->toString())) {
return [];
}
$fullyQualifiedName = $this->resolveFullyQualifiedName($nameNode, $nameNode->toString());
// known types, for performance
if ($fullyQualifiedName === 'PHPUnit\Framework\TestCase') {
return ['PHPUnit\Framework\TestCase'];
}
$classReflection = $this->broker->getClass($fullyQualifiedName);
// skip tests, since they are autoloaded and decoupled from the prod code
// reflection is too performance heavy
if (Strings::contains($fullyQualifiedName, 'Tests') && Strings::endsWith($fullyQualifiedName, 'Test')) {
return [$fullyQualifiedName];
}
return $this->reflectClassLike($fullyQualifiedName);
}
private function shouldSkip(Node $nameNode, string $stringName): bool
{
// is simple name?
if (in_array($stringName, ['true', 'false', 'stdClass'], true)) {
return true;
}
// is parent namespace?
return $nameNode->getAttribute(Attribute::PARENT_NODE) instanceof Namespace_;
}
/**
* @return string[]
*/
private function reflectClassLike(string $name): array
{
$classLikeReflection = $this->smartClassReflector->reflect($name);
if ($classLikeReflection === null) {
return [$name];
}
$useTraits = array_values((array) class_uses($classLikeReflection->getName()));
$implementedInterfaces = $this->smartClassReflector->resolveClassInterfaces($classLikeReflection);
$classParents = $this->smartClassReflector->resolveClassParents($classLikeReflection);
return array_merge([$name], $classParents, $useTraits, $implementedInterfaces);
return $this->classReflectionTypesResolver->resolve($classReflection);
}
private function resolveFullyQualifiedName(Node $nameNode, string $name): string

View File

@ -2,3 +2,16 @@ services:
Rector\NodeTypeResolver\:
resource: '../'
exclude: '../{Contract}'
# PHPStan
PHPStan\Broker\Broker:
factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createBroker']
PHPStan\Analyser\NodeScopeResolver:
factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createNodeScopeResolver']
PHPStan\Analyser\TypeSpecifier:
factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createTypeSpecifier']
PHPStan\Analyser\ScopeFactory:
factory: ['@Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory', 'createScopeFactory']

View File

@ -2,6 +2,6 @@
namespace SomeNamespace;
use Symfony\Component\Process\ProcessBuilder;
use Rector\Symfony\Tests\Rector\Process\ProcessBuilderInstanceRector\Source\ProcessBuilder;
$process = (new ProcessBuilder('something'))->getProcess();

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Symfony\Tests\Rector\Process\ProcessBuilderInstanceRector\Source;
final class ProcessBuilder
{
}

View File

@ -2,6 +2,6 @@
namespace SomeNamespace;
use Symfony\Component\Process\ProcessBuilder;
use Rector\Symfony\Tests\Rector\Process\ProcessBuilderInstanceRector\Source\ProcessBuilder;
$process = ProcessBuilder::create('something')->getProcess();

View File

@ -1,2 +1,3 @@
services:
Rector\Symfony\Rector\Process\ProcessBuilderInstanceRector: ~
Rector\Symfony\Rector\Process\ProcessBuilderInstanceRector:
$processBuilderClass: 'Rector\Symfony\Tests\Rector\Process\ProcessBuilderInstanceRector\Source\ProcessBuilder'

View File

@ -2,9 +2,6 @@ parameters:
ignoreErrors:
# the # after each ignored error is the number of occurrences
# Nette Container
- '#Property Rector\\NodeTypeResolver\\PHPStanNodeScopeResolver::\$(.*?) does not accept object#'
# 0.10.2
# false positives & ->toString() methods
- '#Cannot cast PhpParser\\Node\\Expr\|string to string#' # 8
@ -19,8 +16,9 @@ parameters:
# already fixed, invalidated cache?
- '#Access to an undefined property PhpParser\\Node\\Expr::\$args#'
# class_uses() returns also false actually
- "#Casting to array<string, string> something that's already array<string, string>#"
# nette container
- '#Method Rector\\NodeTypeResolver\\DependencyInjection\\PHPStanServicesFactory::create(.*?)() should return (.*?) but returns object#'
# irelevant
- '#Call to function in_array\(\) with arguments string, array<array<string\|false>> and true will always evaluate to false#'

View File

@ -10,7 +10,6 @@ use PHPStan\Broker\Broker;
use PHPStan\Type\ObjectType;
use Rector\Node\Attribute;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
/**
* Read-only utils for PropertyFetch Node:
@ -23,9 +22,15 @@ final class PropertyFetchAnalyzer
*/
private $nodeTypeResolver;
public function __construct(NodeTypeResolver $nodeTypeResolver)
/**
* @var Broker
*/
private $broker;
public function __construct(NodeTypeResolver $nodeTypeResolver, Broker $broker)
{
$this->nodeTypeResolver = $nodeTypeResolver;
$this->broker = $broker;
}
public function isTypeAndProperty(Node $node, string $type, string $property): bool
@ -149,14 +154,11 @@ final class PropertyFetchAnalyzer
$propertyFetchType = $propertyFetchType->getClassName();
}
/** @var Broker $broker */
$broker = (new PrivatesAccessor())->getPrivateProperty($nodeScope, 'broker');
if (! $broker->hasClass($propertyFetchType)) {
if (! $this->broker->hasClass($propertyFetchType)) {
return false;
}
$classReflection = $broker->getClass($propertyFetchType);
$classReflection = $this->broker->getClass($propertyFetchType);
if (! $classReflection->hasProperty($propertyName)) {
return false;
}

View File

@ -1,3 +1,5 @@
<?php declare (strict_types=1);
use Rector\Tests\Rector\MethodCall\MethodNameReplacerRector\Source\FormMacros;
$result = Nette\Bridges\FormsLatte\Runtime::renderFormBegin($someArgs);

View File

@ -0,0 +1,8 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\MethodCall\MethodNameReplacerRector\Source;
final class FormMacros
{
}

View File

@ -1,3 +1,5 @@
<?php declare (strict_types=1);
$result = Nette\Bridges\FormsLatte\FormMacros::renderFormBegin($someArgs);
use Rector\Tests\Rector\MethodCall\MethodNameReplacerRector\Source\FormMacros;
$result = FormMacros::renderFormBegin($someArgs);

View File

@ -5,5 +5,5 @@ services:
'setDefaultOptions': 'configureOptions'
'Nette\Utils\Html':
'add': 'addHtml'
'Nette\Bridges\FormsLatte\FormMacros':
'Rector\Tests\Rector\MethodCall\MethodNameReplacerRector\Source\FormMacros':
'renderFormBegin': ['Nette\Bridges\FormsLatte\Runtime', 'renderFormBegin']