init MultipleClassFileToPsr4ClassesRector

This commit is contained in:
Tomas Votruba 2018-09-28 19:36:02 +08:00
parent 8a9a9a06e4
commit 488ef3b40a
31 changed files with 702 additions and 43 deletions

View File

@ -50,7 +50,8 @@
"Rector\\PhpParser\\": "packages/PhpParser/src", "Rector\\PhpParser\\": "packages/PhpParser/src",
"Rector\\Doctrine\\": "packages/Doctrine/src", "Rector\\Doctrine\\": "packages/Doctrine/src",
"Rector\\Utils\\": "packages/Utils/src", "Rector\\Utils\\": "packages/Utils/src",
"Rector\\YamlRector\\": "packages/YamlRector/src" "Rector\\YamlRector\\": "packages/YamlRector/src",
"Rector\\FileSystemRector\\": "packages/FileSystemRector/src"
} }
}, },
"autoload-dev": { "autoload-dev": {
@ -67,12 +68,18 @@
"Rector\\Utils\\Tests\\": "packages/Utils/tests", "Rector\\Utils\\Tests\\": "packages/Utils/tests",
"Rector\\Twig\\Tests\\": "packages/Twig/tests", "Rector\\Twig\\Tests\\": "packages/Twig/tests",
"Rector\\Doctrine\\Tests\\": "packages/Doctrine/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": [ "classmap": [
"packages", "packages",
"tests/Issues", "tests/Issues",
"tests/Rector" "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": { "scripts": {

View File

@ -54,6 +54,7 @@ parameters:
# tests files # tests files
- '*tests/*/Wrong/*' - '*tests/*/Wrong/*'
- '*tests/*/Correct/*' - '*tests/*/Correct/*'
- '*tests/*/Expected/*'
skip: skip:
Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer: Symplify\CodingStandard\Fixer\Php\ClassStringToClassConstantFixer:
@ -99,5 +100,6 @@ parameters:
# enforced by interface # enforced by interface
- '*Command.php' - '*Command.php'
- '*NodeVisitor.php' - '*NodeVisitor.php'
- '*CompilerPass.php'
# array type check # array type check
- 'src/RectorDefinition/RectorDefinition.php' - 'src/RectorDefinition/RectorDefinition.php'

View File

@ -0,0 +1,2 @@
imports:
- { resource: 'services.yml' }

View File

@ -0,0 +1,7 @@
services:
_defaults:
public: true
autowire: true
Rector\FileSystemRector\:
resource: '../src'

View File

@ -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;
}

View File

@ -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'
);
}
}

View 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);
}
}

View File

@ -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;
}

View File

@ -7,7 +7,7 @@ use Psr\Container\ContainerInterface;
use Rector\DependencyInjection\ContainerFactory; use Rector\DependencyInjection\ContainerFactory;
use Rector\FileSystem\FileGuard; use Rector\FileSystem\FileGuard;
use Rector\YamlRector\YamlFileProcessor; use Rector\YamlRector\YamlFileProcessor;
use Symfony\Component\Finder\SplFileInfo; use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
use function Safe\sprintf; use function Safe\sprintf;
abstract class AbstractYamlRectorTest extends TestCase abstract class AbstractYamlRectorTest extends TestCase
@ -40,10 +40,9 @@ abstract class AbstractYamlRectorTest extends TestCase
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
{ {
$this->fileGuard->ensureFileExists($file, static::class);
$this->fileGuard->ensureFileExists($reconstructedFile, static::class); $this->fileGuard->ensureFileExists($reconstructedFile, static::class);
$reconstructedFileContent = $this->yamlFileProcessor->processFileInfo(new SplFileInfo($file, '', '')); $reconstructedFileContent = $this->yamlFileProcessor->processFileInfo(new SmartFileInfo($file));
$this->assertStringEqualsFile( $this->assertStringEqualsFile(
$reconstructedFile, $reconstructedFile,

View File

@ -54,7 +54,6 @@ parameters:
- '#Cannot call method getName\(\) on PHPStan\\Reflection\\ClassReflection\|null#' # 1 - '#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#' - '#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 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#' - '#Cannot call method getText\(\) on PhpParser\\Comment\\Doc\|null#'
# Error php-parser token # Error php-parser token

View File

@ -13,6 +13,7 @@ use Rector\Console\Output\ProcessCommandReporter;
use Rector\Console\Shell; use Rector\Console\Shell;
use Rector\ConsoleDiffer\DifferAndFormatter; use Rector\ConsoleDiffer\DifferAndFormatter;
use Rector\FileSystem\FilesFinder; use Rector\FileSystem\FilesFinder;
use Rector\FileSystemRector\FileSystemFileProcessor;
use Rector\Guard\RectorGuard; use Rector\Guard\RectorGuard;
use Rector\Reporting\FileDiff; use Rector\Reporting\FileDiff;
use Rector\YamlRector\YamlFileProcessor; use Rector\YamlRector\YamlFileProcessor;
@ -90,6 +91,11 @@ final class ProcessCommand extends Command
*/ */
private $errors = []; private $errors = [];
/**
* @var FileSystemFileProcessor
*/
private $fileSystemFileProcessor;
public function __construct( public function __construct(
FileProcessor $fileProcessor, FileProcessor $fileProcessor,
ConsoleStyle $consoleStyle, ConsoleStyle $consoleStyle,
@ -99,7 +105,8 @@ final class ProcessCommand extends Command
DifferAndFormatter $differAndFormatter, DifferAndFormatter $differAndFormatter,
AdditionalAutoloader $additionalAutoloader, AdditionalAutoloader $additionalAutoloader,
YamlFileProcessor $yamlFileProcessor, YamlFileProcessor $yamlFileProcessor,
RectorGuard $rectorGuard RectorGuard $rectorGuard,
FileSystemFileProcessor $fileSystemFileProcessor
) { ) {
parent::__construct(); parent::__construct();
@ -112,6 +119,7 @@ final class ProcessCommand extends Command
$this->additionalAutoloader = $additionalAutoloader; $this->additionalAutoloader = $additionalAutoloader;
$this->yamlFileProcessor = $yamlFileProcessor; $this->yamlFileProcessor = $yamlFileProcessor;
$this->rectorGuard = $rectorGuard; $this->rectorGuard = $rectorGuard;
$this->fileSystemFileProcessor = $fileSystemFileProcessor;
} }
protected function configure(): void protected function configure(): void
@ -218,6 +226,33 @@ final class ProcessCommand extends Command
$this->consoleStyle->newLine(2); $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 private function processFile(SplFileInfo $fileInfo): void
{ {
$oldContent = $fileInfo->getContents(); $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 { $this->fileSystemFileProcessor->processFileInfo($fileInfo);
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());
}
} }
} }

View File

@ -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
}
}

View File

@ -5,6 +5,8 @@ namespace Rector\DependencyInjection;
use Rector\Contract\Rector\PhpRectorInterface; use Rector\Contract\Rector\PhpRectorInterface;
use Rector\DependencyInjection\CompilerPass\AutowireInterfacesCompilerPass; use Rector\DependencyInjection\CompilerPass\AutowireInterfacesCompilerPass;
use Rector\DependencyInjection\CompilerPass\CollectorCompilerPass; use Rector\DependencyInjection\CompilerPass\CollectorCompilerPass;
use Rector\FileSystemRector\Contract\FileSystemRectorInterface;
use Rector\FileSystemRector\DependencyInjection\FileSystemRectorCollectorCompilerPass;
use Rector\YamlRector\Contract\YamlRectorInterface; use Rector\YamlRector\Contract\YamlRectorInterface;
use Rector\YamlRector\DependencyInjection\YamlRectorCollectorCompilerPass; use Rector\YamlRector\DependencyInjection\YamlRectorCollectorCompilerPass;
use Symfony\Component\Config\Loader\DelegatingLoader; use Symfony\Component\Config\Loader\DelegatingLoader;
@ -69,16 +71,19 @@ final class RectorKernel extends Kernel
protected function build(ContainerBuilder $containerBuilder): void protected function build(ContainerBuilder $containerBuilder): void
{ {
// collect all Rector services to its runners
$containerBuilder->addCompilerPass(new CollectorCompilerPass()); $containerBuilder->addCompilerPass(new CollectorCompilerPass());
$containerBuilder->addCompilerPass(new YamlRectorCollectorCompilerPass()); $containerBuilder->addCompilerPass(new YamlRectorCollectorCompilerPass());
$containerBuilder->addCompilerPass(new FileSystemRectorCollectorCompilerPass());
// for defaults // for defaults
$containerBuilder->addCompilerPass(new AutowireSinglyImplementedCompilerPass()); $containerBuilder->addCompilerPass(new AutowireSinglyImplementedCompilerPass());
// autowire // autowire Rectors by default (mainly for 3rd party code)
$containerBuilder->addCompilerPass(new AutowireInterfacesCompilerPass([ $containerBuilder->addCompilerPass(new AutowireInterfacesCompilerPass([
PhpRectorInterface::class, PhpRectorInterface::class,
YamlRectorInterface::class, YamlRectorInterface::class,
FileSystemRectorInterface::class,
])); ]));
$containerBuilder->addCompilerPass(new AutoBindParametersCompilerPass()); $containerBuilder->addCompilerPass(new AutoBindParametersCompilerPass());

View File

@ -36,15 +36,6 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$this->expressionAdder = $expressionAdder; $this->expressionAdder = $expressionAdder;
} }
/**
* @param Node[] $nodes
* @return Node[]
*/
final public function beforeTraverse(array $nodes): array
{
return $nodes;
}
/** /**
* @return int|Node|null * @return int|Node|null
*/ */

View 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;
}
}

View File

@ -7,7 +7,7 @@ use Psr\Container\ContainerInterface;
use Rector\Application\FileProcessor; use Rector\Application\FileProcessor;
use Rector\DependencyInjection\ContainerFactory; use Rector\DependencyInjection\ContainerFactory;
use Rector\FileSystem\FileGuard; use Rector\FileSystem\FileGuard;
use Symfony\Component\Finder\SplFileInfo; use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
use Symplify\PackageBuilder\Parameter\ParameterProvider; use Symplify\PackageBuilder\Parameter\ParameterProvider;
use function Safe\sprintf; use function Safe\sprintf;
@ -64,13 +64,11 @@ abstract class AbstractRectorTestCase extends TestCase
protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void protected function doTestFileMatchesExpectedContent(string $file, string $reconstructedFile): void
{ {
$this->fileGuard->ensureFileExists($file, static::class);
$this->fileGuard->ensureFileExists($reconstructedFile, static::class); $this->fileGuard->ensureFileExists($reconstructedFile, static::class);
$this->parameterProvider->changeParameter('source', [$file]); $this->parameterProvider->changeParameter('source', [$file]);
$splFileInfo = new SplFileInfo($file, '', ''); $reconstructedFileContent = $this->fileProcessor->processFileToString(new SmartFileInfo($file));
$reconstructedFileContent = $this->fileProcessor->processFileToString($splFileInfo);
$this->assertStringEqualsFile( $this->assertStringEqualsFile(
$reconstructedFile, $reconstructedFile,

View File

@ -17,6 +17,8 @@ services:
PhpParser\ConstExprEvaluator: PhpParser\ConstExprEvaluator:
factory: ['@Rector\NodeValueResolver\ConstExprEvaluatorFactory', 'create'] factory: ['@Rector\NodeValueResolver\ConstExprEvaluatorFactory', 'create']
Symfony\Component\Filesystem\Filesystem: ~
# Symfony\Console # Symfony\Console
Symfony\Component\Console\Style\SymfonyStyle: ~ Symfony\Component\Console\Style\SymfonyStyle: ~

View File

@ -5,6 +5,7 @@ namespace Rector\Tests\FileSystem\FilesFinder;
use Iterator; use Iterator;
use Rector\FileSystem\FilesFinder; use Rector\FileSystem\FilesFinder;
use Rector\Tests\AbstractContainerAwareTestCase; use Rector\Tests\AbstractContainerAwareTestCase;
use Symfony\Component\Finder\SplFileInfo;
final class FilesFinderTest extends AbstractContainerAwareTestCase final class FilesFinderTest extends AbstractContainerAwareTestCase
{ {
@ -27,6 +28,7 @@ final class FilesFinderTest extends AbstractContainerAwareTestCase
$foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/FilesFinderSource'], $suffixes); $foundFiles = $this->filesFinder->findInDirectoriesAndFiles([__DIR__ . '/FilesFinderSource'], $suffixes);
$this->assertCount($count, $foundFiles); $this->assertCount($count, $foundFiles);
/** @var SplFileInfo $foundFile */
$foundFile = array_pop($foundFiles); $foundFile = array_pop($foundFiles);
$this->assertSame($fileName, $foundFile->getBasename()); $this->assertSame($fileName, $foundFile->getBasename());
} }

View File

@ -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
{
}

View File

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class FirstException
{
}

View File

@ -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
{
}

View File

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class JustOneException
{
}

View File

@ -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
];
}

View File

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class SecondException
{
}

View File

@ -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
{
}

View File

@ -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';
}
}

View File

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class ReadyException
{
}

View File

@ -0,0 +1,7 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class JustOneException
{
}

View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Psr4\MultipleClassFileToPsr4ClassesRector\Source;
final class FirstException
{
}
final class SecondException
{
}

View File

@ -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
];
}

View File

@ -0,0 +1,2 @@
services:
Rector\Rector\Psr4\MultipleClassFileToPsr4ClassesRector: ~