mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 21:38:22 +01:00
init MultipleClassFileToPsr4ClassesRector
This commit is contained in:
parent
8a9a9a06e4
commit
488ef3b40a
@ -50,7 +50,8 @@
|
||||
"Rector\\PhpParser\\": "packages/PhpParser/src",
|
||||
"Rector\\Doctrine\\": "packages/Doctrine/src",
|
||||
"Rector\\Utils\\": "packages/Utils/src",
|
||||
"Rector\\YamlRector\\": "packages/YamlRector/src"
|
||||
"Rector\\YamlRector\\": "packages/YamlRector/src",
|
||||
"Rector\\FileSystemRector\\": "packages/FileSystemRector/src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
@ -67,12 +68,18 @@
|
||||
"Rector\\Utils\\Tests\\": "packages/Utils/tests",
|
||||
"Rector\\Twig\\Tests\\": "packages/Twig/tests",
|
||||
"Rector\\Doctrine\\Tests\\": "packages/Doctrine/tests",
|
||||
"Rector\\YamlRector\\Tests\\": "packages/YamlRector/tests"
|
||||
"Rector\\YamlRector\\Tests\\": "packages/YamlRector/tests",
|
||||
"Rector\\FileSystemRector\\Tests\\": "packages/FileSystemRector/tests"
|
||||
},
|
||||
"classmap": [
|
||||
"packages",
|
||||
"tests/Issues",
|
||||
"tests/Rector"
|
||||
],
|
||||
"files": [
|
||||
"tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Source/exceptions.php",
|
||||
"tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Source/nette-exceptions.php",
|
||||
"tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Source/exception.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
|
2
ecs.yml
2
ecs.yml
@ -54,6 +54,7 @@ parameters:
|
||||
# tests files
|
||||
- '*tests/*/Wrong/*'
|
||||
- '*tests/*/Correct/*'
|
||||
- '*tests/*/Expected/*'
|
||||
|
||||
skip:
|
||||
Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer:
|
||||
@ -99,5 +100,6 @@ parameters:
|
||||
# enforced by interface
|
||||
- '*Command.php'
|
||||
- '*NodeVisitor.php'
|
||||
- '*CompilerPass.php'
|
||||
# array type check
|
||||
- 'src/RectorDefinition/RectorDefinition.php'
|
||||
|
2
packages/FileSystemRector/config/config.yml
Normal file
2
packages/FileSystemRector/config/config.yml
Normal file
@ -0,0 +1,2 @@
|
||||
imports:
|
||||
- { resource: 'services.yml' }
|
7
packages/FileSystemRector/config/services.yml
Normal file
7
packages/FileSystemRector/config/services.yml
Normal file
@ -0,0 +1,7 @@
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
autowire: true
|
||||
|
||||
Rector\FileSystemRector\:
|
||||
resource: '../src'
|
@ -0,0 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\FileSystemRector\Contract;
|
||||
|
||||
use Rector\Contract\Rector\RectorInterface;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
interface FileSystemRectorInterface extends RectorInterface
|
||||
{
|
||||
public function refactor(SplFileInfo $fileInfo): void;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\FileSystemRector\DependencyInjection;
|
||||
|
||||
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
|
||||
use Rector\FileSystemRector\FileSystemFileProcessor;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symplify\PackageBuilder\DependencyInjection\DefinitionCollector;
|
||||
use Symplify\PackageBuilder\DependencyInjection\DefinitionFinder;
|
||||
|
||||
final class FileSystemRectorCollectorCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @var DefinitionCollector
|
||||
*/
|
||||
private $definitionCollector;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->definitionCollector = (new DefinitionCollector(new DefinitionFinder()));
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
$this->definitionCollector->loadCollectorWithType(
|
||||
$containerBuilder,
|
||||
FileSystemFileProcessor::class,
|
||||
FileSystemRectorInterface::class,
|
||||
'addFileSystemRector'
|
||||
);
|
||||
}
|
||||
}
|
39
packages/FileSystemRector/src/FileSystemFileProcessor.php
Normal file
39
packages/FileSystemRector/src/FileSystemFileProcessor.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\FileSystemRector;
|
||||
|
||||
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
final class FileSystemFileProcessor
|
||||
{
|
||||
/**
|
||||
* @var FileSystemRectorInterface[]
|
||||
*/
|
||||
private $fileSystemRectors = [];
|
||||
|
||||
public function addFileSystemRector(FileSystemRectorInterface $fileSystemRector): void
|
||||
{
|
||||
$this->fileSystemRectors[] = $fileSystemRector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileSystemRectorInterface[]
|
||||
*/
|
||||
public function getFileSystemRectors(): array
|
||||
{
|
||||
return $this->fileSystemRectors;
|
||||
}
|
||||
|
||||
public function processFileInfo(SplFileInfo $fileInfo): void
|
||||
{
|
||||
foreach ($this->fileSystemRectors as $fileSystemRector) {
|
||||
$fileSystemRector->refactor($fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public function getFileSystemRectorsCount(): int
|
||||
{
|
||||
return count($this->fileSystemRectors);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\FileSystemRector\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Rector\DependencyInjection\ContainerFactory;
|
||||
use Rector\FileSystemRector\FileSystemFileProcessor;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
abstract class AbstractFileSystemRectorTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* @var FileSystemFileProcessor
|
||||
*/
|
||||
private $fileSystemFileProcessor;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->container = (new ContainerFactory())->createWithConfigFiles([$this->provideConfig()]);
|
||||
|
||||
$this->fileSystemFileProcessor = $this->container->get(FileSystemFileProcessor::class);
|
||||
}
|
||||
|
||||
protected function doTestFile(string $file): void
|
||||
{
|
||||
$this->fileSystemFileProcessor->processFileInfo(new SmartFileInfo($file));
|
||||
}
|
||||
|
||||
abstract protected function provideConfig(): string;
|
||||
}
|
@ -7,7 +7,7 @@ use Psr\Container\ContainerInterface;
|
||||
use Rector\DependencyInjection\ContainerFactory;
|
||||
use Rector\FileSystem\FileGuard;
|
||||
use Rector\YamlRector\YamlFileProcessor;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
use function Safe\sprintf;
|
||||
|
||||
abstract class AbstractYamlRectorTest extends TestCase
|
||||
@ -40,10 +40,9 @@ abstract class AbstractYamlRectorTest extends TestCase
|
||||
|
||||
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
|
||||
{
|
||||
$this->fileGuard->ensureFileExists($file, static::class);
|
||||
$this->fileGuard->ensureFileExists($reconstructedFile, static::class);
|
||||
|
||||
$reconstructedFileContent = $this->yamlFileProcessor->processFileInfo(new SplFileInfo($file, '', ''));
|
||||
$reconstructedFileContent = $this->yamlFileProcessor->processFileInfo(new SmartFileInfo($file));
|
||||
|
||||
$this->assertStringEqualsFile(
|
||||
$reconstructedFile,
|
||||
|
@ -54,7 +54,6 @@ parameters:
|
||||
- '#Cannot call method getName\(\) on PHPStan\\Reflection\\ClassReflection\|null#' # 1
|
||||
- '#Parameter \#1 \$classReflection of method Rector\\NodeTypeResolver\\Reflection\\ClassReflectionTypesResolver::resolve\(\) expects PHPStan\\Reflection\\ClassReflection, PHPStan\\Reflection\\ClassReflection|null given#'
|
||||
- '#Cannot call method getAttribute\(\) on PhpParser\\Node\\Name\|null#'
|
||||
- '#Cannot call method getBasename\(\) on Symfony\\Component\\Finder\\SplFileInfo\|null#'
|
||||
- '#Cannot call method getText\(\) on PhpParser\\Comment\\Doc\|null#'
|
||||
|
||||
# Error php-parser token
|
||||
|
@ -13,6 +13,7 @@ use Rector\Console\Output\ProcessCommandReporter;
|
||||
use Rector\Console\Shell;
|
||||
use Rector\ConsoleDiffer\DifferAndFormatter;
|
||||
use Rector\FileSystem\FilesFinder;
|
||||
use Rector\FileSystemRector\FileSystemFileProcessor;
|
||||
use Rector\Guard\RectorGuard;
|
||||
use Rector\Reporting\FileDiff;
|
||||
use Rector\YamlRector\YamlFileProcessor;
|
||||
@ -90,6 +91,11 @@ final class ProcessCommand extends Command
|
||||
*/
|
||||
private $errors = [];
|
||||
|
||||
/**
|
||||
* @var FileSystemFileProcessor
|
||||
*/
|
||||
private $fileSystemFileProcessor;
|
||||
|
||||
public function __construct(
|
||||
FileProcessor $fileProcessor,
|
||||
ConsoleStyle $consoleStyle,
|
||||
@ -99,7 +105,8 @@ final class ProcessCommand extends Command
|
||||
DifferAndFormatter $differAndFormatter,
|
||||
AdditionalAutoloader $additionalAutoloader,
|
||||
YamlFileProcessor $yamlFileProcessor,
|
||||
RectorGuard $rectorGuard
|
||||
RectorGuard $rectorGuard,
|
||||
FileSystemFileProcessor $fileSystemFileProcessor
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
@ -112,6 +119,7 @@ final class ProcessCommand extends Command
|
||||
$this->additionalAutoloader = $additionalAutoloader;
|
||||
$this->yamlFileProcessor = $yamlFileProcessor;
|
||||
$this->rectorGuard = $rectorGuard;
|
||||
$this->fileSystemFileProcessor = $fileSystemFileProcessor;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
@ -218,6 +226,33 @@ final class ProcessCommand extends Command
|
||||
$this->consoleStyle->newLine(2);
|
||||
}
|
||||
|
||||
private function processFileInfo(SplFileInfo $fileInfo, bool $shouldHideAutoloadErrors): void
|
||||
{
|
||||
try {
|
||||
if ($fileInfo->getExtension() === 'php') {
|
||||
$this->processFile($fileInfo);
|
||||
} elseif (in_array($fileInfo->getExtension(), ['yml', 'yaml'], true)) {
|
||||
$this->processYamlFile($fileInfo);
|
||||
}
|
||||
|
||||
$this->processFileSystemFile($fileInfo);
|
||||
} catch (AnalysedCodeException $analysedCodeException) {
|
||||
if ($shouldHideAutoloadErrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'Analyze error: "%s". Try to include your files in "parameters > autoload_paths".%sSee https://github.com/rectorphp/rector#extra-autoloading',
|
||||
$analysedCodeException->getMessage(),
|
||||
PHP_EOL
|
||||
);
|
||||
|
||||
$this->errors[] = new Error($fileInfo, $message, null);
|
||||
} catch (Throwable $throwable) {
|
||||
$this->errors[] = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
private function processFile(SplFileInfo $fileInfo): void
|
||||
{
|
||||
$oldContent = $fileInfo->getContents();
|
||||
@ -260,28 +295,8 @@ final class ProcessCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function processFileInfo(SplFileInfo $fileInfo, bool $shouldHideAutoloadErrors): void
|
||||
private function processFileSystemFile(SplFileInfo $fileInfo): void
|
||||
{
|
||||
try {
|
||||
if ($fileInfo->getExtension() === 'php') {
|
||||
$this->processFile($fileInfo);
|
||||
} elseif (in_array($fileInfo->getExtension(), ['yml', 'yaml'], true)) {
|
||||
$this->processYamlFile($fileInfo);
|
||||
}
|
||||
} catch (AnalysedCodeException $analysedCodeException) {
|
||||
if ($shouldHideAutoloadErrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'Analyze error: %s Try to include your files in "parameters > autoload_paths".%sSee https://github.com/rectorphp/rector#extra-autoloading',
|
||||
$analysedCodeException->getMessage(),
|
||||
PHP_EOL
|
||||
);
|
||||
|
||||
$this->errors[] = new Error($fileInfo, $message, null);
|
||||
} catch (Throwable $throwable) {
|
||||
$this->errors[] = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
|
||||
}
|
||||
$this->fileSystemFileProcessor->processFileInfo($fileInfo);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\DependencyInjection\CompilerPass;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
final class CollectorConfigCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
// @todo
|
||||
// configure collector via params
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ namespace Rector\DependencyInjection;
|
||||
use Rector\Contract\Rector\PhpRectorInterface;
|
||||
use Rector\DependencyInjection\CompilerPass\AutowireInterfacesCompilerPass;
|
||||
use Rector\DependencyInjection\CompilerPass\CollectorCompilerPass;
|
||||
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
|
||||
use Rector\FileSystemRector\DependencyInjection\FileSystemRectorCollectorCompilerPass;
|
||||
use Rector\YamlRector\Contract\YamlRectorInterface;
|
||||
use Rector\YamlRector\DependencyInjection\YamlRectorCollectorCompilerPass;
|
||||
use Symfony\Component\Config\Loader\DelegatingLoader;
|
||||
@ -69,16 +71,19 @@ final class RectorKernel extends Kernel
|
||||
|
||||
protected function build(ContainerBuilder $containerBuilder): void
|
||||
{
|
||||
// collect all Rector services to its runners
|
||||
$containerBuilder->addCompilerPass(new CollectorCompilerPass());
|
||||
$containerBuilder->addCompilerPass(new YamlRectorCollectorCompilerPass());
|
||||
$containerBuilder->addCompilerPass(new FileSystemRectorCollectorCompilerPass());
|
||||
|
||||
// for defaults
|
||||
$containerBuilder->addCompilerPass(new AutowireSinglyImplementedCompilerPass());
|
||||
|
||||
// autowire
|
||||
// autowire Rectors by default (mainly for 3rd party code)
|
||||
$containerBuilder->addCompilerPass(new AutowireInterfacesCompilerPass([
|
||||
PhpRectorInterface::class,
|
||||
YamlRectorInterface::class,
|
||||
FileSystemRectorInterface::class,
|
||||
]));
|
||||
|
||||
$containerBuilder->addCompilerPass(new AutoBindParametersCompilerPass());
|
||||
|
@ -36,15 +36,6 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
$this->expressionAdder = $expressionAdder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
* @return Node[]
|
||||
*/
|
||||
final public function beforeTraverse(array $nodes): array
|
||||
{
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|null
|
||||
*/
|
||||
|
225
src/Rector/Psr4/MultipleClassFileToPsr4ClassesRector.php
Normal file
225
src/Rector/Psr4/MultipleClassFileToPsr4ClassesRector.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Rector\Psr4;
|
||||
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
|
||||
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
|
||||
use Rector\Parser\Parser;
|
||||
use Rector\Printer\FormatPerservingPrinter;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Utils\BetterNodeFinder;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
final class MultipleClassFileToPsr4ClassesRector implements FileSystemRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var Filesystem
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var Lexer
|
||||
*/
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* @var FormatPerservingPrinter
|
||||
*/
|
||||
private $formatPerservingPrinter;
|
||||
|
||||
/**
|
||||
* @var NodeScopeAndMetadataDecorator
|
||||
*/
|
||||
private $nodeScopeAndMetadataDecorator;
|
||||
|
||||
public function __construct(
|
||||
BetterNodeFinder $betterNodeFinder,
|
||||
Filesystem $filesystem,
|
||||
Parser $parser,
|
||||
Lexer $lexer,
|
||||
FormatPerservingPrinter $formatPerservingPrinter,
|
||||
NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator
|
||||
) {
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->filesystem = $filesystem;
|
||||
$this->parser = $parser;
|
||||
$this->lexer = $lexer;
|
||||
$this->formatPerservingPrinter = $formatPerservingPrinter;
|
||||
$this->nodeScopeAndMetadataDecorator = $nodeScopeAndMetadataDecorator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Turns namespaced classes in one file to standalone PSR-4 classes.',
|
||||
[
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class FirstException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
final class SecondException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
// new file: "app/Exceptions/FirstException.php"
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class FirstException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// new file: "app/Exceptions/SecondException.php"
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
final class SecondException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function refactor(SplFileInfo $fileInfo): void
|
||||
{
|
||||
$oldStmts = $this->parser->parseFile($fileInfo->getRealPath());
|
||||
|
||||
// needed for format preserving
|
||||
$newStmts = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($oldStmts, $fileInfo->getRealPath());
|
||||
|
||||
/** @var Namespace_[] $namespaceNodes */
|
||||
$namespaceNodes = $this->betterNodeFinder->findInstanceOf($newStmts, Namespace_::class);
|
||||
|
||||
if ($this->shouldSkip($fileInfo, $newStmts, $namespaceNodes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($namespaceNodes as $namespaceNode) {
|
||||
$newStmtsSet = $this->removeAllOtherNamespaces($newStmts, $namespaceNode);
|
||||
|
||||
foreach ($newStmtsSet as $newStmt) {
|
||||
if (! $newStmt instanceof Namespace_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var Class_[] $namespacedClassNodes */
|
||||
$namespacedClassNodes = $this->betterNodeFinder->findInstanceOf($newStmt->stmts, Class_::class);
|
||||
|
||||
foreach ($namespacedClassNodes as $classNode) {
|
||||
$this->removeAllClassesFromNamespaceNode($newStmt);
|
||||
$newStmt->stmts[] = $classNode;
|
||||
|
||||
// reindex from 0, for the printer
|
||||
$newStmt->stmts = array_values($newStmt->stmts);
|
||||
|
||||
$fileDestination = $this->createClassFileDestination($classNode, $fileInfo);
|
||||
|
||||
$fileContent = $this->formatPerservingPrinter->printToString(
|
||||
$newStmtsSet,
|
||||
$oldStmts,
|
||||
$this->lexer->getTokens()
|
||||
);
|
||||
|
||||
$this->filesystem->dumpFile($fileDestination, $fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function createClassFileDestination(Class_ $classNode, SplFileInfo $fileInfo): string
|
||||
{
|
||||
$currentDirectory = dirname($fileInfo->getRealPath());
|
||||
|
||||
return $currentDirectory . DIRECTORY_SEPARATOR . (string) $classNode->name . '.php';
|
||||
}
|
||||
|
||||
private function removeAllClassesFromNamespaceNode(Namespace_ $namespaceNode): void
|
||||
{
|
||||
foreach ($namespaceNode->stmts as $key => $namespaceStatement) {
|
||||
if ($namespaceStatement instanceof Class_) {
|
||||
unset($namespaceNode->stmts[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
* @return Node[]
|
||||
*/
|
||||
private function removeAllOtherNamespaces(array $nodes, Namespace_ $namespaceNode): array
|
||||
{
|
||||
foreach ($nodes as $key => $stmt) {
|
||||
if ($stmt instanceof Namespace_ && $stmt !== $namespaceNode) {
|
||||
unset($nodes[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// reindex from 0, for the printer
|
||||
return array_values($nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
* @param Namespace_[] $namespaceNodes
|
||||
*/
|
||||
private function shouldSkip(SplFileInfo $fileInfo, array $nodes, array $namespaceNodes): bool
|
||||
{
|
||||
// process only namespaced file
|
||||
if (! $namespaceNodes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var Class_[] $classNodes */
|
||||
$classNodes = $this->betterNodeFinder->findInstanceOf($nodes, Class_::class);
|
||||
|
||||
$nonAnonymousClassNodes = array_filter($classNodes, function (Class_ $classNode) {
|
||||
return $classNode->name;
|
||||
});
|
||||
|
||||
// only process file with multiple classes || class with non PSR-4 format
|
||||
if (! $nonAnonymousClassNodes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (count($nonAnonymousClassNodes) === 1) {
|
||||
$nonAnonymousClassNode = $nonAnonymousClassNodes[0];
|
||||
if ((string) $nonAnonymousClassNode->name === $fileInfo->getFilename()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use Psr\Container\ContainerInterface;
|
||||
use Rector\Application\FileProcessor;
|
||||
use Rector\DependencyInjection\ContainerFactory;
|
||||
use Rector\FileSystem\FileGuard;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
use function Safe\sprintf;
|
||||
|
||||
@ -64,13 +64,11 @@ abstract class AbstractRectorTestCase extends TestCase
|
||||
|
||||
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
|
||||
{
|
||||
$this->fileGuard->ensureFileExists($file, static::class);
|
||||
$this->fileGuard->ensureFileExists($reconstructedFile, static::class);
|
||||
|
||||
$this->parameterProvider->changeParameter('source', [$file]);
|
||||
|
||||
$splFileInfo = new SplFileInfo($file, '', '');
|
||||
$reconstructedFileContent = $this->fileProcessor->processFileToString($splFileInfo);
|
||||
$reconstructedFileContent = $this->fileProcessor->processFileToString(new SmartFileInfo($file));
|
||||
|
||||
$this->assertStringEqualsFile(
|
||||
$reconstructedFile,
|
||||
|
@ -17,6 +17,8 @@ services:
|
||||
PhpParser\ConstExprEvaluator:
|
||||
factory: ['@Rector\NodeValueResolver\ConstExprEvaluatorFactory', 'create']
|
||||
|
||||
Symfony\Component\Filesystem\Filesystem: ~
|
||||
|
||||
# Symfony\Console
|
||||
Symfony\Component\Console\Style\SymfonyStyle: ~
|
||||
|
||||
|
@ -5,6 +5,7 @@ namespace Rector\Tests\FileSystem\FilesFinder;
|
||||
use Iterator;
|
||||
use Rector\FileSystem\FilesFinder;
|
||||
use Rector\Tests\AbstractContainerAwareTestCase;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
final class FilesFinderTest extends AbstractContainerAwareTestCase
|
||||
{
|
||||
@ -27,6 +28,7 @@ final class FilesFinderTest extends AbstractContainerAwareTestCase
|
||||
$foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/FilesFinderSource'], $suffixes);
|
||||
$this->assertCount($count, $foundFiles);
|
||||
|
||||
/** @var SplFileInfo $foundFile */
|
||||
$foundFile = array_pop($foundFiles);
|
||||
$this->assertSame($fileName, $foundFile->getBasename());
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* The exception that is thrown when the value of an argument is
|
||||
* outside the allowable range of values as defined by the invoked method.
|
||||
*/
|
||||
class ArgumentOutOfRangeException extends InvalidArgumentException
|
||||
{
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class FirstException
|
||||
{
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a method call is invalid for the object's
|
||||
* current state, method has been invoked at an illegal or inappropriate time.
|
||||
*/
|
||||
class InvalidStateException extends \RuntimeException
|
||||
{
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class JustOneException
|
||||
{
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload\Utils;
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
public const MESSAGES = [
|
||||
PREG_INTERNAL_ERROR => 'Internal error',
|
||||
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
|
||||
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
|
||||
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point',
|
||||
6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR
|
||||
];
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class SecondException
|
||||
{
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload\Utils;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates invalid image file.
|
||||
*/
|
||||
class UnknownImageFileException extends RegexpException
|
||||
{
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\FileSystemRector\Tests\AbstractFileSystemRectorTest;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
/**
|
||||
* @see \Rector\Rector\Psr4\MultipleClassFileToPsr4ClassesRector
|
||||
*/
|
||||
final class MultipleClassFileToPsr4ClassesRectorTest extends AbstractFileSystemRectorTest
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if (! $this->getProvidedData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cleanup filesystem
|
||||
$generatedFiles = array_keys($this->getProvidedData()[1]);
|
||||
(new Filesystem())->remove($generatedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $expectedExceptions
|
||||
* @dataProvider provideExceptionsData
|
||||
*/
|
||||
public function test(string $file, array $expectedExceptions): void
|
||||
{
|
||||
$this->doTestFile($file);
|
||||
|
||||
foreach ($expectedExceptions as $expectedExceptionLocation => $expectedFormat) {
|
||||
$this->assertFileExists($expectedExceptionLocation);
|
||||
$this->assertFileEquals($expectedFormat, $expectedExceptionLocation);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideExceptionsData(): Iterator
|
||||
{
|
||||
yield [
|
||||
__DIR__ . '/Source/exceptions.php',
|
||||
[
|
||||
__DIR__ . '/Source/FirstException.php' => __DIR__ . '/Expected/FirstException.php',
|
||||
__DIR__ . '/Source/SecondException.php' => __DIR__ . '/Expected/SecondException.php',
|
||||
],
|
||||
];
|
||||
|
||||
// source: https://github.com/nette/utils/blob/798f8c1626a8e0e23116d90e588532725cce7d0e/src/Utils/exceptions.php
|
||||
yield [
|
||||
__DIR__ . '/Source/nette-exceptions.php',
|
||||
[
|
||||
__DIR__ . '/Source/ArgumentOutOfRangeException.php' => __DIR__ . '/Expected/ArgumentOutOfRangeException.php',
|
||||
__DIR__ . '/Source/InvalidStateException.php' => __DIR__ . '/Expected/InvalidStateException.php',
|
||||
__DIR__ . '/Source/RegexpException.php' => __DIR__ . '/Expected/RegexpException.php',
|
||||
__DIR__ . '/Source/UnknownImageFileException.php' => __DIR__ . '/Expected/UnknownImageFileException.php',
|
||||
],
|
||||
];
|
||||
|
||||
// non PSR-4 file with one class
|
||||
yield [
|
||||
__DIR__ . '/Source/exception.php',
|
||||
[
|
||||
__DIR__ . '/Source/JustOneException.php' => __DIR__ . '/Expected/JustOneException.php',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSkip(): void
|
||||
{
|
||||
$originalFileContent = (new SmartFileInfo(__DIR__ . '/Source/ReadyException.php'))->getContents();
|
||||
|
||||
$this->doTestFile(__DIR__ . '/Source/ReadyException.php');
|
||||
$this->assertStringEqualsFile(__DIR__ . '/Source/ReadyException.php', $originalFileContent);
|
||||
}
|
||||
|
||||
protected function provideConfig(): string
|
||||
{
|
||||
return __DIR__ . '/config.yml';
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class ReadyException
|
||||
{
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class JustOneException
|
||||
{
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
|
||||
|
||||
final class FirstException
|
||||
{
|
||||
}
|
||||
|
||||
final class SecondException
|
||||
{
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* The exception that is thrown when the value of an argument is
|
||||
* outside the allowable range of values as defined by the invoked method.
|
||||
*/
|
||||
class ArgumentOutOfRangeException extends InvalidArgumentException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a method call is invalid for the object's
|
||||
* current state, method has been invoked at an illegal or inappropriate time.
|
||||
*/
|
||||
class InvalidStateException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
namespace NettePostfixedToUniqueAutoload\Utils;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates invalid image file.
|
||||
*/
|
||||
class UnknownImageFileException extends RegexpException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
public const MESSAGES = [
|
||||
PREG_INTERNAL_ERROR => 'Internal error',
|
||||
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
|
||||
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
|
||||
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point',
|
||||
6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR
|
||||
];
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
services:
|
||||
Rector\Rector\Psr4\MultipleClassFileToPsr4ClassesRector: ~
|
Loading…
x
Reference in New Issue
Block a user