mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 11:50:51 +00:00
[SymfonyPHPConfig] Add monorepo split for value objects function (#4153)
* add monorepo split for symfony-php-config * [SOLID] Prevent replacing referenced parametes with inlined constnat value * make Symfon 5.0- compatible * add support for reference skip in constructor * [rector] add support for reference skip in constructor * [cs] add support for reference skip in constructor * improve misisng rule feedback * underscore * Fix ChangeReadOnlyVariableWithDefaultValueToConstantRector for new * add clear-cache to all commands * colors * [rector] colors * [cs] colors Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
parent
35537dba84
commit
4a76cdaa82
6
.github/workflows/code_analysis.yaml
vendored
6
.github/workflows/code_analysis.yaml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
-
|
||||
name: 'Show command'
|
||||
run: bin/rector show --config rector-ci.php
|
||||
run: bin/rector show --config rector-ci.php --ansi
|
||||
|
||||
-
|
||||
name: Validate Fixtures
|
||||
|
@ -49,6 +49,10 @@ jobs:
|
|||
name: 'PHP Linter'
|
||||
run: vendor/bin/parallel-lint src tests packages --exclude packages/rector-generator/templates
|
||||
|
||||
-
|
||||
name: 'Validate Monorepo'
|
||||
run: composer validate-monorepo
|
||||
|
||||
name: ${{ matrix.actions.name }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ expectedArguments(
|
|||
\Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::CLOSURE_NODE,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
|
||||
);
|
||||
|
||||
expectedArguments(
|
||||
|
@ -79,6 +80,7 @@ expectedArguments(
|
|||
\Rector\NodeTypeResolver\Node\AttributeKey::VIRTUAL_NODE,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::CLOSURE_NODE,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::PARAMETER_POSITION,
|
||||
\Rector\NodeTypeResolver\Node\AttributeKey::ARGUMENT_POSITION,
|
||||
);
|
||||
|
||||
expectedArguments(
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
|
||||
use Rector\Caching\Detector\ChangedFilesDetector;
|
||||
use Rector\Core\Bootstrap\ConfigShifter;
|
||||
use Rector\Core\Bootstrap\NoRectorsLoadedReporter;
|
||||
use Rector\Core\Configuration\Configuration;
|
||||
use Rector\Core\Configuration\MinimalVersionChecker;
|
||||
use Rector\Core\Configuration\MinimalVersionChecker\ComposerJsonParser;
|
||||
|
@ -13,11 +14,11 @@ use Rector\Core\Bootstrap\RectorConfigsResolver;
|
|||
use Rector\Core\Console\Application;
|
||||
use Rector\Core\Console\Style\SymfonyStyleFactory;
|
||||
use Rector\Core\DependencyInjection\RectorContainerFactory;
|
||||
use Rector\Core\Exception\NoRectorsLoadedException;
|
||||
use Symplify\SetConfigResolver\Bootstrap\InvalidSetReporter;
|
||||
use Symplify\PackageBuilder\Console\ShellCode;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
use Symplify\SetConfigResolver\Exception\SetNotFoundException;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
@ini_set('memory_limit', '-1'); // @ intentionally: continue anyway
|
||||
|
||||
|
@ -78,6 +79,7 @@ try {
|
|||
exit(ShellCode::ERROR);
|
||||
}
|
||||
|
||||
/** @var Application $application */
|
||||
$application = $container->get(Application::class);
|
||||
exit($application->run());
|
||||
|
||||
|
|
|
@ -152,11 +152,11 @@
|
|||
"Rector\\DowngradePhp73\\": "rules/downgrade-php73/src",
|
||||
"Rector\\DowngradePhp74\\": "rules/downgrade-php74/src",
|
||||
"Rector\\DowngradePhp80\\": "rules/downgrade-php80/src",
|
||||
"Rector\\SymfonyPhpConfig\\": "rules/symfony-php-config/src"
|
||||
"Rector\\SymfonyPhpConfig\\": ["packages/symfony-php-config/src", "rules/symfony-php-config/src"]
|
||||
},
|
||||
"files": [
|
||||
"rules/symfony-php-config/functions/functions.php",
|
||||
"rules/restoration/tests/Rector/Use_/RestoreFullyQualifiedNameRector/Source/ShortClassOnly.php"
|
||||
"rules/restoration/tests/Rector/Use_/RestoreFullyQualifiedNameRector/Source/ShortClassOnly.php",
|
||||
"packages/symfony-php-config/functions/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
@ -243,7 +243,7 @@
|
|||
"Rector\\DowngradePhp73\\Tests\\": "rules/downgrade-php73/tests",
|
||||
"Rector\\DowngradePhp74\\Tests\\": "rules/downgrade-php74/tests",
|
||||
"Rector\\DowngradePhp80\\Tests\\": "rules/downgrade-php80/tests",
|
||||
"Rector\\SymfonyPhpConfig\\Tests\\": "rules/symfony-php-config/tests",
|
||||
"Rector\\SymfonyPhpConfig\\Tests\\": ["rules/symfony-php-config/tests", "packages/symfony-php-config/tests"],
|
||||
"Rector\\PHPStanStaticTypeMapper\\Tests\\": "packages/phpstan-static-type-mapper/tests"
|
||||
},
|
||||
"classmap": [
|
||||
|
@ -274,7 +274,8 @@
|
|||
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/AnotherClass.php",
|
||||
"rules/coding-style/tests/Rector/Namespace_/ImportFullyQualifiedNamesRector/Source/YetAnotherClass.php",
|
||||
"rules/solid/tests/Rector/ClassMethod/UseInterfaceOverImplementationInConstructorRector/Source/Coconut.php",
|
||||
"stubs/Nette/Localization/ITranslation.php"
|
||||
"stubs/Nette/Localization/ITranslation.php",
|
||||
"vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -310,7 +311,10 @@
|
|||
],
|
||||
"rector-ci": "bin/rector process --config rector-ci.php --dry-run --ansi",
|
||||
"rector": "bin/rector process --config rector-ci.php --ansi",
|
||||
"release": "vendor/bin/monorepo-builder release patch --ansi"
|
||||
"release": "vendor/bin/monorepo-builder release patch --ansi",
|
||||
"merge": "vendor/bin/monorepo-builder merge --ansi",
|
||||
"propagate": "vendor/bin/monorepo-builder propagate --ansi",
|
||||
"validate-monorepo": "vendor/bin/monorepo-builder validate --ansi"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
|
|
|
@ -11,6 +11,7 @@ use PhpParser\NodeFinder;
|
|||
use PhpParser\NodeVisitor\CloningVisitor;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\ParserFactory;
|
||||
use Rector\Core\Bootstrap\NoRectorsLoadedReporter;
|
||||
use Rector\Core\Configuration\MinimalVersionChecker;
|
||||
use Rector\Core\Console\Application;
|
||||
use Rector\Core\EventDispatcher\AutowiredEventDispatcher;
|
||||
|
@ -63,6 +64,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
$services->alias(SymfonyApplication::class, Application::class);
|
||||
|
||||
$services->set(NoRectorsLoadedReporter::class);
|
||||
|
||||
$services->set(TextDescriptor::class);
|
||||
|
||||
$services->set(ParserFactory::class);
|
||||
|
|
|
@ -13,4 +13,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
// release workers - in order to execute
|
||||
$services->set(Symplify\MonorepoBuilder\Release\ReleaseWorker\TagVersionReleaseWorker::class);
|
||||
$services->set(Symplify\MonorepoBuilder\Release\ReleaseWorker\PushTagReleaseWorker::class);
|
||||
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::DIRECTORIES_TO_REPOSITORIES, [
|
||||
__DIR__ . '/packages/symfony-php-config' => 'git@github.com:rectorphp/symfony-php-config.git',
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -119,7 +119,7 @@ final class ConsoleOutputFormatter implements OutputFormatterInterface
|
|||
$this->symfonyStyle->newLine();
|
||||
|
||||
if ($fileDiff->getRectorChanges() !== []) {
|
||||
$this->symfonyStyle->writeln('Applied rules:');
|
||||
$this->symfonyStyle->writeln('<options=underscore>Applied rules:</>');
|
||||
$this->symfonyStyle->newLine();
|
||||
$this->symfonyStyle->listing($fileDiff->getRectorClasses());
|
||||
$this->symfonyStyle->newLine();
|
||||
|
|
|
@ -199,4 +199,9 @@ final class AttributeKey
|
|||
* @var string
|
||||
*/
|
||||
public const PARAMETER_POSITION = 'parameter_position';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const ARGUMENT_POSITION = 'argument_position';
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use PhpParser\NodeVisitor\NodeConnectingVisitor;
|
|||
use Rector\Core\Configuration\Configuration;
|
||||
use Rector\NodeCollector\NodeVisitor\NodeCollectorNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\FileInfoNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\FunctionLikeParamPositionNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\FunctionLikeParamArgPositionNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\FunctionMethodAndClassNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\NamespaceNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\PhpDocInfoNodeVisitor;
|
||||
|
@ -74,9 +74,9 @@ final class NodeScopeAndMetadataDecorator
|
|||
private $nodeConnectingVisitor;
|
||||
|
||||
/**
|
||||
* @var FunctionLikeParamPositionNodeVisitor
|
||||
* @var FunctionLikeParamArgPositionNodeVisitor
|
||||
*/
|
||||
private $functionLikeParamPositionNodeVisitor;
|
||||
private $functionLikeParamArgPositionNodeVisitor;
|
||||
|
||||
public function __construct(
|
||||
CloningVisitor $cloningVisitor,
|
||||
|
@ -89,7 +89,7 @@ final class NodeScopeAndMetadataDecorator
|
|||
PhpDocInfoNodeVisitor $phpDocInfoNodeVisitor,
|
||||
StatementNodeVisitor $statementNodeVisitor,
|
||||
NodeConnectingVisitor $nodeConnectingVisitor,
|
||||
FunctionLikeParamPositionNodeVisitor $functionLikeParamPositionNodeVisitor
|
||||
FunctionLikeParamArgPositionNodeVisitor $functionLikeParamArgPositionNodeVisitor
|
||||
) {
|
||||
$this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver;
|
||||
$this->cloningVisitor = $cloningVisitor;
|
||||
|
@ -101,7 +101,7 @@ final class NodeScopeAndMetadataDecorator
|
|||
$this->configuration = $configuration;
|
||||
$this->phpDocInfoNodeVisitor = $phpDocInfoNodeVisitor;
|
||||
$this->nodeConnectingVisitor = $nodeConnectingVisitor;
|
||||
$this->functionLikeParamPositionNodeVisitor = $functionLikeParamPositionNodeVisitor;
|
||||
$this->functionLikeParamArgPositionNodeVisitor = $functionLikeParamArgPositionNodeVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,7 +141,7 @@ final class NodeScopeAndMetadataDecorator
|
|||
$nodeTraverser->addVisitor($this->functionMethodAndClassNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->namespaceNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->phpDocInfoNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->functionLikeParamPositionNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->functionLikeParamArgPositionNodeVisitor);
|
||||
|
||||
$nodes = $nodeTraverser->traverse($nodes);
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class FunctionLikeParamArgPositionNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @return Node
|
||||
*/
|
||||
public function enterNode(Node $node): ?Node
|
||||
{
|
||||
if ($node instanceof FunctionLike) {
|
||||
foreach ($node->getParams() as $position => $param) {
|
||||
$param->setAttribute(AttributeKey::PARAMETER_POSITION, $position);
|
||||
}
|
||||
}
|
||||
|
||||
if ($node instanceof MethodCall || $node instanceof StaticCall || $node instanceof FuncCall || $node instanceof New_) {
|
||||
foreach ($node->args as $position => $arg) {
|
||||
$arg->setAttribute(AttributeKey::ARGUMENT_POSITION, $position);
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class FunctionLikeParamPositionNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @return Node
|
||||
*/
|
||||
public function enterNode(Node $node): ?Node
|
||||
{
|
||||
if (! $node instanceof FunctionLike) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->getParams() as $position => $param) {
|
||||
$param->setAttribute(AttributeKey::PARAMETER_POSITION, $position);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
37
packages/symfony-php-config/composer.json
Normal file
37
packages/symfony-php-config/composer.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "rector/symfony-php-config",
|
||||
"description": "Tools that easy work with Symfony PHP Configs",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tomas Votruba",
|
||||
"email": "tomas.vot@gmail.com",
|
||||
"homepage": "https://tomasvotruba.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2.4|^8.0",
|
||||
"symfony/dependency-injection": "^4.4.8|^5.1",
|
||||
"symfony/http-kernel": "^4.4.8|^5.0.6",
|
||||
"symplify/package-builder": "^8.3.29"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5|^9.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Rector\\SymfonyPhpConfig\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"functions/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Rector\\SymfonyPhpConfig\\Tests\\": "tests"
|
||||
},
|
||||
"files": [
|
||||
"../../vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\SymfonyPhpConfig;
|
||||
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\SymfonyPhpConfig\Exception\ValueObjectException;
|
||||
use Rector\SymfonyPhpConfig\Reflection\ArgumentAndParameterFactory;
|
||||
use ReflectionClass;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\InlineServiceConfigurator;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\ref;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator;
|
||||
|
@ -27,10 +28,10 @@ function inline_single_object(object $object, ServicesConfigurator $servicesConf
|
|||
$servicesConfigurator->set(ArgumentAndParameterFactory::class);
|
||||
|
||||
$servicesConfigurator->set($className)
|
||||
->factory([service(ArgumentAndParameterFactory::class), 'create'])
|
||||
->factory([ref(ArgumentAndParameterFactory::class), 'create'])
|
||||
->args([$className, $argumentValues, $propertyValues]);
|
||||
|
||||
return service($className);
|
||||
return ref($className);
|
||||
}
|
||||
|
||||
function inline_value_object(object $object): InlineServiceConfigurator
|
||||
|
@ -78,11 +79,11 @@ function resolve_argument_values(ReflectionClass $reflectionClass, object $objec
|
|||
'Constructor for "%s" was not found. Be sure to use only value objects',
|
||||
$reflectionClass->getName()
|
||||
);
|
||||
throw new ShouldNotHappenException($message);
|
||||
throw new ValueObjectException($message);
|
||||
}
|
||||
|
||||
foreach ($constructorMethodReflection->getParameters() as $constructorParameter) {
|
||||
$parameterName = $constructorParameter->getName();
|
||||
foreach ($constructorMethodReflection->getParameters() as $reflectionParameter) {
|
||||
$parameterName = $reflectionParameter->getName();
|
||||
$propertyReflection = $reflectionClass->getProperty($parameterName);
|
||||
$propertyReflection->setAccessible(true);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyPhpConfig\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class ValueObjectException extends Exception
|
||||
{
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyPhpConfig\Tests\Functions;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use function Rector\SymfonyPhpConfig\inline_single_object;
|
||||
use Rector\SymfonyPhpConfig\Tests\Functions\Source\SomeValueObject;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
final class InlineValueObjectTest extends TestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$servicesConfigurator = $this->createServiceConfigurator();
|
||||
|
||||
$someValueObject = new SomeValueObject('Rector');
|
||||
$referenceConfigurator = inline_single_object($someValueObject, $servicesConfigurator);
|
||||
|
||||
$this->assertInstanceOf(ReferenceConfigurator::class, $referenceConfigurator);
|
||||
|
||||
$id = (string) $referenceConfigurator;
|
||||
$this->assertSame(SomeValueObject::class, $id);
|
||||
}
|
||||
|
||||
private function createServiceConfigurator(): ServicesConfigurator
|
||||
{
|
||||
$containerBuilder = new ContainerBuilder();
|
||||
$phpFileLoader = new PhpFileLoader($containerBuilder, new FileLocator());
|
||||
|
||||
$instanceOf = [];
|
||||
|
||||
return new ServicesConfigurator($containerBuilder, $phpFileLoader, $instanceOf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SymfonyPhpConfig\Tests\Functions\Source;
|
||||
|
||||
final class SomeValueObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
|
@ -51,10 +51,19 @@ final class ExistingClassesProvider
|
|||
$composerJsonFilePath = getcwd() . '/composer.json';
|
||||
$composerJson = $this->composerJsonFactory->createFromFilePath($composerJsonFilePath);
|
||||
|
||||
$psr4Paths = $composerJson->getAutoload()['psr-4'] ?? [];
|
||||
$directories = [];
|
||||
|
||||
foreach ($composerJson->getAutoload()['psr-4'] ?? [] as $paths) {
|
||||
if (is_array($paths)) {
|
||||
$directories = array_merge($directories, $paths);
|
||||
} else {
|
||||
$directories[] = $paths;
|
||||
}
|
||||
}
|
||||
|
||||
$classmapPaths = $composerJson->getAutoload()['classmap'] ?? [];
|
||||
|
||||
return array_merge($psr4Paths, $classmapPaths);
|
||||
return array_merge($directories, $classmapPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -180,12 +180,12 @@ CODE_SAMPLE
|
|||
|
||||
/** @var Variable|ClassConstFetch $variable */
|
||||
$variable = $readOnlyVariableAssign->var;
|
||||
// already overriden
|
||||
// already overridden
|
||||
if (! $variable instanceof Variable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classConst = $this->createClassConst($variable, $readOnlyVariableAssign->expr);
|
||||
$classConst = $this->createPrivateClassConst($variable, $readOnlyVariableAssign->expr);
|
||||
|
||||
// replace $variable usage in the code with constant
|
||||
$this->addConstantToClass($class, $classConst);
|
||||
|
@ -199,7 +199,7 @@ CODE_SAMPLE
|
|||
}
|
||||
}
|
||||
|
||||
private function createClassConst(Variable $variable, Expr $expr): ClassConst
|
||||
private function createPrivateClassConst(Variable $variable, Expr $expr): ClassConst
|
||||
{
|
||||
$constantName = $this->createConstantNameFromVariable($variable);
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
class SkipReferenceInSerivceConfigurator
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$containerBuilder = $this->createContainerBuilder();
|
||||
$phpFileLoader = $this->createPhpFileLoader($containerBuilder);
|
||||
|
||||
$instanceOf = [];
|
||||
return new ServicesConfigurator($containerBuilder, $phpFileLoader, $instanceOf);
|
||||
}
|
||||
|
||||
private function createPhpFileLoader(ContainerBuilder $containerBuilder): PhpFileLoader
|
||||
{
|
||||
return new PhpFileLoader($containerBuilder, new FileLocator());
|
||||
}
|
||||
|
||||
private function createContainerBuilder(): ContainerBuilder
|
||||
{
|
||||
return new ContainerBuilder();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
class SkipReferenced
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$value = [];
|
||||
$this->process($value);
|
||||
}
|
||||
|
||||
private function process(array &$value)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector\Fixture;
|
||||
|
||||
use Rector\SOLID\Tests\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector\Source\ReferenceInConstructor;
|
||||
|
||||
class SkipReferencedInConstructor
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$value = 'name';
|
||||
$servicesConfigurator = new ReferenceInConstructor($value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\SOLID\Tests\Rector\Class_\ChangeReadOnlyVariableWithDefaultValueToConstantRector\Source;
|
||||
|
||||
final class ReferenceInConstructor
|
||||
{
|
||||
public function __construct(string &$value)
|
||||
{
|
||||
}
|
||||
}
|
42
src/Bootstrap/NoRectorsLoadedReporter.php
Normal file
42
src/Bootstrap/NoRectorsLoadedReporter.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Bootstrap;
|
||||
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
final class NoRectorsLoadedReporter
|
||||
{
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
private $symfonyStyle;
|
||||
|
||||
public function __construct(SymfonyStyle $symfonyStyle)
|
||||
{
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
}
|
||||
|
||||
public function report(): void
|
||||
{
|
||||
$this->symfonyStyle->error('We could not find any Rector rules to run');
|
||||
|
||||
$this->symfonyStyle->writeln('You have few options to add them:');
|
||||
$this->symfonyStyle->newLine();
|
||||
|
||||
$this->symfonyStyle->title('Add single rule to "rector.php"');
|
||||
$this->symfonyStyle->writeln(' $services = $containerConfigurator->services();');
|
||||
$this->symfonyStyle->writeln(' $services->set(...);');
|
||||
$this->symfonyStyle->newLine(2);
|
||||
|
||||
$this->symfonyStyle->title('Add set of rules to "rector.php"');
|
||||
$this->symfonyStyle->writeln(' $parameters = $containerConfigurator->parameters();');
|
||||
$this->symfonyStyle->writeln(' $parameters->set(Option::SET, [...]);');
|
||||
$this->symfonyStyle->newLine(2);
|
||||
|
||||
$this->symfonyStyle->title('Missing "rector.php" in your project? Let Rector create it for you');
|
||||
$this->symfonyStyle->writeln(' vendor/bin/rector init');
|
||||
$this->symfonyStyle->newLine();
|
||||
}
|
||||
}
|
|
@ -55,7 +55,6 @@ final class RectorConfigsResolver
|
|||
|
||||
/**
|
||||
* @return SmartFileInfo[]
|
||||
* @noRector
|
||||
*/
|
||||
public function provide(): array
|
||||
{
|
||||
|
|
|
@ -155,4 +155,19 @@ final class Option
|
|||
* @var string
|
||||
*/
|
||||
public const OPTION_DEBUG = 'debug';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const XDEBUG = 'xdebug';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const OPTION_SET = 'set';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const OPTION_CONFIG = 'config';
|
||||
}
|
||||
|
|
|
@ -8,8 +8,11 @@ use Composer\XdebugHandler\XdebugHandler;
|
|||
use OutOfBoundsException;
|
||||
use Rector\ChangesReporting\Output\CheckstyleOutputFormatter;
|
||||
use Rector\ChangesReporting\Output\JsonOutputFormatter;
|
||||
use Rector\Core\Bootstrap\NoRectorsLoadedReporter;
|
||||
use Rector\Core\Configuration\Configuration;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\Exception\Configuration\InvalidConfigurationException;
|
||||
use Rector\Core\Exception\NoRectorsLoadedException;
|
||||
use Rector\DocumentationGenerator\Command\DumpRectorsCommand;
|
||||
use Rector\Utils\NodeDocumentationGenerator\Command\DumpNodesCommand;
|
||||
use Symfony\Component\Console\Application as SymfonyApplication;
|
||||
|
@ -20,6 +23,7 @@ use Symfony\Component\Console\Input\InputOption;
|
|||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symplify\PackageBuilder\Console\Command\CommandNaming;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Throwable;
|
||||
|
||||
final class Application extends SymfonyApplication
|
||||
{
|
||||
|
@ -33,11 +37,19 @@ final class Application extends SymfonyApplication
|
|||
*/
|
||||
private $configuration;
|
||||
|
||||
/**
|
||||
* @var NoRectorsLoadedReporter
|
||||
*/
|
||||
private $noRectorsLoadedReporter;
|
||||
|
||||
/**
|
||||
* @param Command[] $commands
|
||||
*/
|
||||
public function __construct(Configuration $configuration, array $commands = [])
|
||||
{
|
||||
public function __construct(
|
||||
Configuration $configuration,
|
||||
NoRectorsLoadedReporter $noRectorsLoadedReporter,
|
||||
array $commands = []
|
||||
) {
|
||||
try {
|
||||
$version = $configuration->getPrettyVersion();
|
||||
} catch (OutOfBoundsException $outOfBoundsException) {
|
||||
|
@ -48,6 +60,7 @@ final class Application extends SymfonyApplication
|
|||
|
||||
$this->addCommands($commands);
|
||||
$this->configuration = $configuration;
|
||||
$this->noRectorsLoadedReporter = $noRectorsLoadedReporter;
|
||||
}
|
||||
|
||||
public function doRun(InputInterface $input, OutputInterface $output): int
|
||||
|
@ -100,6 +113,17 @@ final class Application extends SymfonyApplication
|
|||
return parent::doRun($input, $output);
|
||||
}
|
||||
|
||||
public function renderThrowable(Throwable $throwable, OutputInterface $output): void
|
||||
{
|
||||
if (is_a($throwable, NoRectorsLoadedException::class)) {
|
||||
$this->noRectorsLoadedReporter->report();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Change the autogenerated stub
|
||||
parent::renderThrowable($throwable, $output);
|
||||
}
|
||||
|
||||
protected function getDefaultInputDefinition(): InputDefinition
|
||||
{
|
||||
$defaultInputDefinition = parent::getDefaultInputDefinition();
|
||||
|
@ -150,7 +174,7 @@ final class Application extends SymfonyApplication
|
|||
private function addCustomOptions(InputDefinition $inputDefinition): void
|
||||
{
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
'config',
|
||||
Option::OPTION_CONFIG,
|
||||
'c',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Path to config file',
|
||||
|
@ -158,26 +182,33 @@ final class Application extends SymfonyApplication
|
|||
));
|
||||
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
'set',
|
||||
Option::OPTION_SET,
|
||||
's',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Finds config by shortcut name'
|
||||
));
|
||||
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
'debug',
|
||||
Option::OPTION_DEBUG,
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Enable debug verbosity (-vvv)'
|
||||
));
|
||||
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
'xdebug',
|
||||
Option::XDEBUG,
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Allow running xdebug'
|
||||
));
|
||||
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
Option::OPTION_CLEAR_CACHE,
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Clear cache'
|
||||
));
|
||||
|
||||
$inputDefinition->addOption(new InputOption(
|
||||
'--working-dir',
|
||||
'-d',
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Core\Console\Command;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Caching\Detector\ChangedFilesDetector;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Descriptor\TextDescriptor;
|
||||
|
@ -15,6 +16,11 @@ use Symplify\PackageBuilder\Console\ShellCode;
|
|||
|
||||
abstract class AbstractCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var ChangedFilesDetector
|
||||
*/
|
||||
protected $changedFilesDetector;
|
||||
|
||||
/**
|
||||
* @var TextDescriptor
|
||||
*/
|
||||
|
@ -23,9 +29,12 @@ abstract class AbstractCommand extends Command
|
|||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireAbstractCommand(TextDescriptor $textDescriptor): void
|
||||
{
|
||||
public function autowireAbstractCommand(
|
||||
TextDescriptor $textDescriptor,
|
||||
ChangedFilesDetector $changedFilesDetector
|
||||
): void {
|
||||
$this->textDescriptor = $textDescriptor;
|
||||
$this->changedFilesDetector = $changedFilesDetector;
|
||||
}
|
||||
|
||||
public function run(InputInterface $input, OutputInterface $output): int
|
||||
|
@ -63,6 +72,14 @@ abstract class AbstractCommand extends Command
|
|||
|
||||
$this->getApplication()
|
||||
->setCatchExceptions(false);
|
||||
|
||||
// clear cache
|
||||
$this->changedFilesDetector->clear();
|
||||
}
|
||||
|
||||
// clear cache
|
||||
if ($input->getOption(Option::OPTION_CLEAR_CACHE)) {
|
||||
$this->changedFilesDetector->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,11 +86,6 @@ final class ProcessCommand extends AbstractCommand
|
|||
*/
|
||||
private $unchangedFilesFilter;
|
||||
|
||||
/**
|
||||
* @var ChangedFilesDetector
|
||||
*/
|
||||
private $changedFilesDetector;
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
|
|
|
@ -25,12 +25,6 @@ final class RectorGuard
|
|||
return;
|
||||
}
|
||||
|
||||
// @todo @dx display nicer way instead of all red, as in https://github.com/symplify/symplify/blame/master/packages/easy-coding-standard/bin/ecs#L69-L83
|
||||
throw new NoRectorsLoadedException(sprintf(
|
||||
'We need some rectors to run:%s* register them in rector.php under "services():"%s* use "--set <set>"%s* or use "--config <file>.yaml"',
|
||||
PHP_EOL,
|
||||
PHP_EOL,
|
||||
PHP_EOL
|
||||
));
|
||||
throw new NoRectorsLoadedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Rector\Core\PHPStan\Reflection;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Analyser\Scope;
|
||||
|
@ -16,7 +17,10 @@ use PHPStan\Reflection\MethodReflection;
|
|||
use PHPStan\Reflection\ParametersAcceptor;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\PHPStan\Reflection\TypeToCallReflectionResolver\TypeToCallReflectionResolverRegistry;
|
||||
use Rector\Core\ValueObject\MethodName;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
@ -55,6 +59,27 @@ final class CallReflectionResolver
|
|||
$this->typeToCallReflectionResolverRegistry = $typeToCallReflectionResolverRegistry;
|
||||
}
|
||||
|
||||
public function resolveConstructor(New_ $new): ?MethodReflection
|
||||
{
|
||||
/** @var Scope|null $scope */
|
||||
$scope = $new->getAttribute(AttributeKey::SCOPE);
|
||||
if ($scope === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classType = $this->nodeTypeResolver->resolve($new->class);
|
||||
|
||||
if ($classType instanceof UnionType) {
|
||||
return $this->matchConstructorMethodInUnionType($classType, $scope);
|
||||
}
|
||||
|
||||
if (! $classType->hasMethod(MethodName::CONSTRUCT)->yes()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $classType->getMethod(MethodName::CONSTRUCT, $scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FuncCall|MethodCall|StaticCall $node
|
||||
* @return MethodReflection|FunctionReflection|null
|
||||
|
@ -70,7 +95,7 @@ final class CallReflectionResolver
|
|||
|
||||
/**
|
||||
* @param FunctionReflection|MethodReflection|null $reflection
|
||||
* @param FuncCall|MethodCall|StaticCall $node
|
||||
* @param FuncCall|MethodCall|StaticCall|New_ $node
|
||||
*/
|
||||
public function resolveParametersAcceptor($reflection, Node $node): ?ParametersAcceptor
|
||||
{
|
||||
|
@ -98,6 +123,22 @@ final class CallReflectionResolver
|
|||
return ParametersAcceptorSelector::selectFromArgs($scope, $node->args, $variants);
|
||||
}
|
||||
|
||||
private function matchConstructorMethodInUnionType(UnionType $unionType, Scope $scope): ?MethodReflection
|
||||
{
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if (! $unionedType instanceof TypeWithClassName) {
|
||||
continue;
|
||||
}
|
||||
if (! $unionedType->hasMethod(MethodName::CONSTRUCT)->yes()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $unionedType->getMethod(MethodName::CONSTRUCT, $scope);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FunctionReflection|MethodReflection|null
|
||||
*/
|
||||
|
|
|
@ -11,17 +11,22 @@ use PhpParser\Node\Expr\Assign;
|
|||
use PhpParser\Node\Expr\ClosureUse;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\List_;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Foreach_;
|
||||
use PHPStan\Reflection\ObjectTypeMethodReflection;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
|
@ -57,13 +62,19 @@ final class ClassMethodAssignManipulator
|
|||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
/**
|
||||
* @var CallReflectionResolver
|
||||
*/
|
||||
private $callReflectionResolver;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
NodeFactory $nodeFactory,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
VariableManipulator $variableManipulator
|
||||
VariableManipulator $variableManipulator,
|
||||
CallReflectionResolver $callReflectionResolver
|
||||
) {
|
||||
$this->variableManipulator = $variableManipulator;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
|
@ -71,6 +82,7 @@ final class ClassMethodAssignManipulator
|
|||
$this->nodeFactory = $nodeFactory;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->callReflectionResolver = $callReflectionResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,41 +167,7 @@ final class ClassMethodAssignManipulator
|
|||
*/
|
||||
private function filterOutReferencedVariables(array $variableAssigns, ClassMethod $classMethod): array
|
||||
{
|
||||
$referencedVariables = [];
|
||||
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use (
|
||||
&$referencedVariables
|
||||
) {
|
||||
if (! $node instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if ($parentNode !== null && $this->isExplicitlyReferenced($parentNode)) {
|
||||
/** @var string $variableName */
|
||||
$variableName = $this->nodeNameResolver->getName($node);
|
||||
$referencedVariables[] = $variableName;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof Arg) {
|
||||
$parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE);
|
||||
}
|
||||
|
||||
if (! $parentNode instanceof FuncCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isNames($parentNode, ['array_shift', '*sort'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($parentNode->args[0]->value === $node) {
|
||||
/** @var string $variableName */
|
||||
$variableName = $this->nodeNameResolver->getName($node);
|
||||
$referencedVariables[] = $variableName;
|
||||
}
|
||||
});
|
||||
$referencedVariables = $this->collectReferenceVariableNames($classMethod);
|
||||
|
||||
return array_filter($variableAssigns, function (Assign $assign) use ($referencedVariables): bool {
|
||||
return ! $this->nodeNameResolver->isNames($assign->var, $referencedVariables);
|
||||
|
@ -247,13 +225,55 @@ final class ClassMethodAssignManipulator
|
|||
return false;
|
||||
}
|
||||
|
||||
private function isExplicitlyReferenced(Node $node): bool
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function collectReferenceVariableNames(ClassMethod $classMethod): array
|
||||
{
|
||||
if ($node instanceof Arg || $node instanceof ClosureUse || $node instanceof Param) {
|
||||
return $node->byRef;
|
||||
}
|
||||
$referencedVariables = [];
|
||||
|
||||
return false;
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use (
|
||||
&$referencedVariables
|
||||
) {
|
||||
if (! $node instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->nodeNameResolver->isName($node, 'this')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if ($parentNode !== null && $this->isExplicitlyReferenced($parentNode)) {
|
||||
/** @var string $variableName */
|
||||
$variableName = $this->nodeNameResolver->getName($node);
|
||||
$referencedVariables[] = $variableName;
|
||||
return null;
|
||||
}
|
||||
|
||||
$argumentPosition = null;
|
||||
if ($parentNode instanceof Arg) {
|
||||
$argumentPosition = $parentNode->getAttribute(AttributeKey::ARGUMENT_POSITION);
|
||||
$parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE);
|
||||
}
|
||||
|
||||
if (! $parentNode instanceof Node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($argumentPosition === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $variableName */
|
||||
$variableName = $this->nodeNameResolver->getName($node);
|
||||
|
||||
if ($this->isCallOrConstructorWithReference($parentNode, $node, $argumentPosition)) {
|
||||
$referencedVariables[] = $variableName;
|
||||
}
|
||||
});
|
||||
|
||||
return $referencedVariables;
|
||||
}
|
||||
|
||||
private function findParentForeach(Assign $assign): ?Foreach_
|
||||
|
@ -266,4 +286,105 @@ final class ClassMethodAssignManipulator
|
|||
|
||||
return $foreach;
|
||||
}
|
||||
|
||||
private function isExplicitlyReferenced(Node $node): bool
|
||||
{
|
||||
if ($node instanceof Arg || $node instanceof ClosureUse || $node instanceof Param) {
|
||||
return $node->byRef;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isCallOrConstructorWithReference(Node $node, Variable $variable, int $argumentPosition): bool
|
||||
{
|
||||
if ($this->isMethodCallWithReferencedArgument($node, $variable)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isFuncCallWithReferencedArgument($node, $variable)) {
|
||||
return true;
|
||||
}
|
||||
return $this->isConstructorWithReference($node, $argumentPosition);
|
||||
}
|
||||
|
||||
private function isMethodCallWithReferencedArgument(Node $node, Variable $variable): bool
|
||||
{
|
||||
if (! $node instanceof MethodCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodReflection = $this->callReflectionResolver->resolveCall($node);
|
||||
if (! $methodReflection instanceof ObjectTypeMethodReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$variableName = $this->nodeNameResolver->getName($variable);
|
||||
$parametersAcceptor = $this->callReflectionResolver->resolveParametersAcceptor($methodReflection, $node);
|
||||
if ($parametersAcceptor === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var ParameterReflection $parameter */
|
||||
foreach ($parametersAcceptor->getParameters() as $parameter) {
|
||||
if ($parameter->getName() !== $variableName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $parameter->passedByReference()
|
||||
->yes();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches e.g:
|
||||
* - array_shift($value)
|
||||
* - sort($values)
|
||||
*/
|
||||
private function isFuncCallWithReferencedArgument(Node $node, Variable $variable): bool
|
||||
{
|
||||
if (! $node instanceof FuncCall) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->nodeNameResolver->isNames($node, ['array_shift', '*sort'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// is 1t argument
|
||||
return $node->args[0]->value !== $variable;
|
||||
}
|
||||
|
||||
private function isConstructorWithReference(Node $node, int $argumentPosition): bool
|
||||
{
|
||||
if (! $node instanceof New_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isParameterReferencedInMethodReflection($node, $argumentPosition);
|
||||
}
|
||||
|
||||
private function isParameterReferencedInMethodReflection(New_ $new, int $argumentPosition): bool
|
||||
{
|
||||
$methodReflection = $this->callReflectionResolver->resolveConstructor($new);
|
||||
$parametersAcceptor = $this->callReflectionResolver->resolveParametersAcceptor($methodReflection, $new);
|
||||
|
||||
if ($parametersAcceptor === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var ParameterReflection $parameter */
|
||||
foreach ($parametersAcceptor->getParameters() as $parameterPosition => $parameter) {
|
||||
if ($parameterPosition !== $argumentPosition) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $parameter->passedByReference()
|
||||
->yes();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
22
travis.yml
Normal file
22
travis.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
# inspired by https://github.com/rectorphp/rector/blob/master/.travis.yml
|
||||
language: php
|
||||
|
||||
dist: bionic
|
||||
|
||||
php: 7.4
|
||||
|
||||
before_install:
|
||||
- phpenv config-rm xdebug.ini
|
||||
|
||||
jobs:
|
||||
include:
|
||||
-
|
||||
name: "Split Monorepo"
|
||||
if: (branch = master OR tag IS present) && type = push
|
||||
script:
|
||||
- composer update
|
||||
# travis_retry to prevent fails
|
||||
- travis_retry vendor/bin/monorepo-builder split --ansi
|
||||
|
||||
notifications:
|
||||
email: false
|
Loading…
Reference in New Issue
Block a user