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
5 changes: 1 addition & 4 deletions build/target-repository/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,5 @@
"rector/rector-downgrade-php": "*"
},
"minimum-stability": "dev",
"prefer-stable": true,
"suggest": {
"ext-dom": "To manipulate phpunit.xml via the custom-rule command"
}
"prefer-stable": true
}
122 changes: 5 additions & 117 deletions src/Console/Command/CustomRuleCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,143 +4,31 @@

namespace Rector\Console\Command;

use Nette\Utils\FileSystem;
use Nette\Utils\Strings;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Enum\ClassName;
use Rector\Exception\ShouldNotHappenException;
use Rector\FileSystem\JsonFileSystem;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;

final class CustomRuleCommand extends Command
{
public function __construct(
private readonly SymfonyStyle $symfonyStyle,
private readonly ReflectionProvider $reflectionProvider
private readonly SymfonyStyle $symfonyStyle
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('custom-rule');
$this->setDescription('Create base of local custom rule with tests');
$this->setDescription('[DEPRECATED] Create base of local custom rule with tests');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
// ask for rule name
$rectorName = $this->symfonyStyle->ask(
'What is the rule class name? (e.g. "LegacyCallToDbalMethodCall")?',
null,
static function (?string $answer): string {
if ($answer === '' || $answer === null) {
throw new ShouldNotHappenException('Rector name cannot be empty');
}

return $answer;
}
$this->symfonyStyle->error(
'The "custom-rule" command is deprecated and no longer generates files. Use an AI agent to scaffold a custom rule instead - it handles the setup faster and with less guesswork.'
);

// suffix with Rector by convention
if (! str_ends_with((string) $rectorName, 'Rector')) {
$rectorName .= 'Rector';
}

$rectorName = ucfirst((string) $rectorName);

// find all files in templates directory
$finder = Finder::create()
->files()
->in(__DIR__ . '/../../../templates/custom-rule')
->notName('__Name__Test.php.phtml');

// 0. resolve if local phpunit is at least PHPUnit 10 (which supports #[DataProvider])
// to provide annotation if not
if ($this->isPHPUnitAttributeSupported()) {
$finder->append([
new SplFileInfo(
__DIR__ . '/../../../templates/custom-rule/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml',
'utils/rector/tests/Rector/__Name__',
'utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml',
),
]);
} else {
// use @annotations for PHPUnit 9 and bellow
$finder->append([
new SplFileInfo(
__DIR__ . '/../../../templates/custom-rules-annotations/utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml',
'utils/rector/tests/Rector/__Name__',
'utils/rector/tests/Rector/__Name__/__Name__Test.php.phtml',
),
]);
}

$currentDirectory = getcwd();

$generatedFilePaths = [];

$fileInfos = iterator_to_array($finder->getIterator());

foreach ($fileInfos as $fileInfo) {
// replace "__Name__" with $rectorName
$newContent = $this->replaceNameVariable($rectorName, $fileInfo->getContents());
$newFilePath = $this->replaceNameVariable($rectorName, $fileInfo->getRelativePathname());

// remove "phtml" suffix
$newFilePath = Strings::substring($newFilePath, 0, -strlen('.phtml'));

FileSystem::write($currentDirectory . '/' . $newFilePath, $newContent, null);

$generatedFilePaths[] = $newFilePath;
}

$title = sprintf('Skeleton for "%s" rule was created. Now write rule logic to solve your problem', $rectorName);
$this->symfonyStyle->title($title);
$this->symfonyStyle->listing($generatedFilePaths);

// 2. update autoload-dev in composer.json
$composerJsonFilePath = $currentDirectory . '/composer.json';
if (file_exists($composerJsonFilePath)) {
$hasChanged = false;
$composerJson = JsonFileSystem::readFilePath($composerJsonFilePath);

if (! isset($composerJson['autoload-dev']['psr-4']['Utils\\Rector\\'])) {
$composerJson['autoload-dev']['psr-4']['Utils\\Rector\\'] = 'utils/rector/src';
$composerJson['autoload-dev']['psr-4']['Utils\\Rector\\Tests\\'] = 'utils/rector/tests';
$hasChanged = true;
}

if ($hasChanged) {
$this->symfonyStyle->writeln('We updated "composer.json" autoload-dev to load Rector rules.');
$this->symfonyStyle->writeln('Now run "composer dump-autoload" to update paths');
JsonFileSystem::writeFile($composerJsonFilePath, $composerJson);
}
}

$this->symfonyStyle->newLine(1);

// 3. update phpunit.xml(.dist) to include rector test suite
$this->symfonyStyle->writeln('<fg=green>Run Rector tests via PHPUnit:</>');
$this->symfonyStyle->newLine(1);
$this->symfonyStyle->writeln(' vendor/bin/phpunit utils/rector/tests');
$this->symfonyStyle->newLine(1);

return Command::SUCCESS;
}

private function replaceNameVariable(string $rectorName, string $contents): string
{
return str_replace('__Name__', $rectorName, $contents);
}

private function isPHPUnitAttributeSupported(): bool
{
return $this->reflectionProvider->hasClass(ClassName::DATA_PROVIDER);
return Command::FAILURE;
}
}
2 changes: 0 additions & 2 deletions src/Enum/ClassName.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,4 @@ final class ClassName
public const string JMS_TYPE = 'JMS\Serializer\Annotation\Type';

public const string DOCTRINE_ENTITY = 'Doctrine\ORM\Mapping\Entity';

public const string DATA_PROVIDER = 'PHPUnit\Framework\Attributes\DataProvider';
}
9 changes: 0 additions & 9 deletions src/FileSystem/JsonFileSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,4 @@ public static function readFilePath(string $filePath): array

return Json::decode($fileContents, forceArrays: true);
}

/**
* @param array<string, mixed> $data
*/
public static function writeFile(string $filePath, array $data): void
{
$json = Json::encode($data, pretty: true);
FileSystem::write($filePath, $json, null);
}
}
33 changes: 0 additions & 33 deletions templates/custom-rule/utils/rector/src/Rector/__Name__.php.phtml

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading