Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages-tests/Skipper/Skipper/SkipperRectorRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Skipper\Skipper;

use Illuminate\Container\RewindableGenerator;
use Rector\Core\Configuration\Option;
use Rector\Core\Configuration\Parameter\SimpleParameterProvider;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\FileSystem\PhpFilesFinder;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
use Rector\Testing\PHPUnit\AbstractLazyTestCase;

final class SkipperRectorRuleTest extends AbstractLazyTestCase
{
protected function tearDown(): void
{
// cleanup configuration
SimpleParameterProvider::setParameter(Option::SKIP, []);
}

public function testRemovingServiceFromContainer(): void
{
// register 2 rules, but one is skipped
$this->bootFromConfigFiles([__DIR__ . '/config/single_skipped_rule_config.php']);

$container = self::getContainer();

// to invoke before resolving
$container->make(PhpFilesFinder::class);

// here 1 rule should be removed and 1 should remain
/** @var RewindableGenerator<int, RectorInterface> $rectorsIterator */
$rectorsIterator = $container->tagged(RectorInterface::class);
$this->assertCount(1, $rectorsIterator);

$rectors = iterator_to_array($rectorsIterator->getIterator());
$this->assertInstanceOf(RemoveUnusedPromotedPropertyRector::class, $rectors[0]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([
InlineConstructorDefaultToPropertyRector::class,
RemoveUnusedPromotedPropertyRector::class,
]);

$rectorConfig->skip([InlineConstructorDefaultToPropertyRector::class]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Rector\Skipper\SkipCriteriaResolver;

use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Configuration\Option;
use Rector\Core\Configuration\Parameter\SimpleParameterProvider;
use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment;
Expand All @@ -16,11 +15,6 @@ final class SkippedClassResolver
*/
private array $skippedClasses = [];

public function __construct(
private readonly ReflectionProvider $reflectionProvider
) {
}

/**
* @return array<string, string[]|null>
*/
Expand Down Expand Up @@ -49,7 +43,8 @@ public function resolve(): array
continue;
}

if (! $this->reflectionProvider->hasClass($key)) {
// this only checks for Rector rules, that are always autoloaded
if (! class_exists($key) && ! interface_exists($key)) {
continue;
}

Expand Down
6 changes: 1 addition & 5 deletions packages/Testing/PHPUnit/AbstractLazyTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Rector\Core\DependencyInjection\LazyContainerFactory;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\Reflection\PrivatesAccessor;
use Webmozart\Assert\Assert;

abstract class AbstractLazyTestCase extends TestCase
{
Expand All @@ -26,10 +25,7 @@ protected function bootFromConfigFiles(array $configFiles): void
$rectorConfig = self::getContainer();

foreach ($configFiles as $configFile) {
$configClosure = require $configFile;
Assert::isCallable($configClosure);

$configClosure($rectorConfig);
$rectorConfig->import($configFile);
}
}

Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -645,3 +645,8 @@ parameters:
-
message: '#Parameters should use "PhpParser\\Node\\Stmt\\ClassMethod" types as the only types passed to this method#'
path: packages/VendorLocker/ParentClassMethodTypeOverrideGuard.php

# checks for rector always autoloaded rules only
-
message: '#Function "(class_exists|interface_exists)\(\)" cannot be used/left in the code\: use ReflectionProvider\->has\*\(\) instead#'
path: packages/Skipper/SkipCriteriaResolver/SkippedClassResolver.php
36 changes: 36 additions & 0 deletions src/DependencyInjection/Laravel/ContainerMemento.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Rector\Core\DependencyInjection\Laravel;

use Illuminate\Container\Container;
use Rector\Core\Util\Reflection\PrivatesAccessor;

/**
* Helper service to modify Laravel container
*/
final class ContainerMemento
{
public static function forgetService(Container $container, string $typeToForget): void
{
// 1. remove the service
$container->offsetUnset($typeToForget);

// 2. remove all tagged rules
$privatesAccessor = new PrivatesAccessor();
$privatesAccessor->propertyClosure($container, 'tags', static function (array $tags) use (
$typeToForget
): array {
foreach ($tags as $tagName => $taggedClasses) {
foreach ($taggedClasses as $key => $taggedClass) {
if (is_a($taggedClass, $typeToForget, true)) {
unset($tags[$tagName][$key]);
}
}
}

return $tags;
});
}
}
34 changes: 27 additions & 7 deletions src/DependencyInjection/LazyContainerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
use Rector\Core\Contract\Processor\FileProcessorInterface;
use Rector\Core\Contract\Rector\PhpRectorInterface;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\DependencyInjection\Laravel\ContainerMemento;
use Rector\Core\Logging\CurrentRectorProvider;
use Rector\Core\Logging\RectorOutput;
use Rector\Core\NodeDecorator\CreatedByRuleDecorator;
Expand Down Expand Up @@ -165,6 +166,7 @@
use Rector\PostRector\Application\PostFileProcessor;
use Rector\RectorGenerator\Command\GenerateCommand;
use Rector\RectorGenerator\Command\InitRecipeCommand;
use Rector\Skipper\SkipCriteriaResolver\SkippedClassResolver;
use Rector\Skipper\Skipper\Skipper;
use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface;
use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
Expand Down Expand Up @@ -401,11 +403,10 @@ public function create(): RectorConfig
$rectorConfig->containerCacheDirectory(sys_get_temp_dir());

// make use of https://github.com/symplify/easy-parallel

$rectorConfig->singleton(Application::class, static function (): Application {
$application = new Application();

// @todo inject commands

$privatesAccessor = new PrivatesAccessor();
$privatesAccessor->propertyClosure($application, 'commands', static function (array $commands): array {
unset($commands['completion']);
Expand All @@ -416,17 +417,17 @@ public function create(): RectorConfig
return $application;
});

$rectorConfig->singleton(Inflector::class, static function (): Inflector {
$inflectorFactory = new InflectorFactory();
return $inflectorFactory->build();
});

$rectorConfig->singleton(ConsoleApplication::class, ConsoleApplication::class);

$rectorConfig->when(ConsoleApplication::class)
->needs('$commands')
->giveTagged(Command::class);

$rectorConfig->singleton(Inflector::class, static function (): Inflector {
$inflectorFactory = new InflectorFactory();
return $inflectorFactory->build();
});

$rectorConfig->tag(ProcessCommand::class, Command::class);
$rectorConfig->tag(WorkerCommand::class, Command::class);
$rectorConfig->tag(SetupCICommand::class, Command::class);
Expand Down Expand Up @@ -731,6 +732,25 @@ static function (
->needs('$phpDocNodeVisitors')
->giveTagged(BasePhpDocNodeVisitorInterface::class);

/** @param mixed $parameters */
$hasForgotten = false;
$rectorConfig->beforeResolving(static function (string $abstract, array $parameters, Container $container) use (
&$hasForgotten
): void {
// run only once
if ($hasForgotten && ! defined('PHPUNIT_COMPOSER_INSTALL')) {
return;
}

$skippedClassResolver = new SkippedClassResolver();
$skippedClasses = array_keys($skippedClassResolver->resolve());
foreach ($skippedClasses as $skippedClass) {
ContainerMemento::forgetService($container, $skippedClass);
}

$hasForgotten = true;
});

return $rectorConfig;
}

Expand Down