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
39 changes: 30 additions & 9 deletions packages/FileSystemRector/src/Rector/AbstractFileSystemRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ protected function printNewNodesToFilePath(array $nodes, string $fileDestination
if ($this->areStringsSameWithoutSpaces($formatPreservingContent, $prettyPrintContent)) {
$fileContent = $formatPreservingContent;
} else {
$prettyPrintContent = $this->resolveLastEmptyLine($prettyPrintContent);
$fileContent = $prettyPrintContent;
}

Expand All @@ -167,26 +168,46 @@ protected function addFile(string $filePath, string $content): void
*/
private function areStringsSameWithoutSpaces(string $firstString, string $secondString): bool
{
// remove all comments
return $this->clearString($firstString) === $this->clearString($secondString);
}

// remove all spaces
$firstString = Strings::replace($firstString, '#\s+#', '');
$secondString = Strings::replace($secondString, '#\s+#', '');
private function clearString(string $string): string
{
$string = $this->removeComments($string);

$firstString = $this->removeComments($firstString);
$secondString = $this->removeComments($secondString);
// remove all spaces
$string = Strings::replace($string, '#\s+#', '');

// remove FQN "\" that are added by basic printer
$firstString = Strings::replace($firstString, '#\\\\#', '');
$secondString = Strings::replace($secondString, '#\\\\#', '');
$string = Strings::replace($string, '#\\\\#', '');

return $firstString === $secondString;
// remove trailing commas, as one of them doesn't have to contain them
return Strings::replace($string, '#\,#', '');
}

private function removeComments(string $string): string
{
// remove comments like this noe
$string = Strings::replace($string, '#\/\/(.*?)\n#', '');

$string = Strings::replace($string, '#/\*.*?\*/#s', '');

return Strings::replace($string, '#\n\s*\n#', "\n");
}

/**
* Add empty line in the end, if it is in the original tokens
*/
private function resolveLastEmptyLine(string $prettyPrintContent): string
{
$tokens = $this->lexer->getTokens();
$lastToken = array_pop($tokens);
if ($lastToken) {
if (Strings::contains($lastToken[1], "\n")) {
$prettyPrintContent = trim($prettyPrintContent) . PHP_EOL;
}
}

return $prettyPrintContent;
}
}
164 changes: 80 additions & 84 deletions src/Rector/Psr4/MultipleClassFileToPsr4ClassesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,29 @@
namespace Rector\Rector\Psr4;

use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\Namespace_;
use Rector\CodingStyle\Naming\ClassNaming;
use Rector\FileSystemRector\Rector\AbstractFileSystemRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;

final class MultipleClassFileToPsr4ClassesRector extends AbstractFileSystemRector
{
/**
* @var ClassNaming
*/
private $classNaming;

public function __construct(ClassNaming $classNaming)
{
$this->classNaming = $classNaming;
}

public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
Expand Down Expand Up @@ -67,91 +77,22 @@ final class SecondException extends Exception
public function refactor(SmartFileInfo $smartFileInfo): void
{
$nodes = $this->parseFileInfoToNodes($smartFileInfo);

if ($this->shouldSkip($smartFileInfo, $nodes)) {
return;
}

$shouldDelete = false;
$shouldDelete = $this->shouldDeleteFileInfo($smartFileInfo, $nodes);

/** @var Namespace_[] $namespaceNodes */
$namespaceNodes = $this->betterNodeFinder->findInstanceOf($nodes, Namespace_::class);

if (count($namespaceNodes)) {
$shouldDelete = $this->processNamespaceNodes($smartFileInfo, $namespaceNodes, $nodes);
$this->processNamespaceNodes($smartFileInfo, $namespaceNodes, $nodes, $shouldDelete);
} else {
// process only files with 2 classes and more
$classes = $this->betterNodeFinder->findClassLikes($nodes);

if (count($classes) <= 1) {
return;
}

$declareNode = null;
foreach ($nodes as $node) {
if ($node instanceof Declare_) {
$declareNode = $node;
}

if (! $node instanceof Class_ || $node->isAnonymous()) {
continue;
}

$fileDestination = $this->createClassLikeFileDestination($node, $smartFileInfo);

if ($smartFileInfo->getRealPath() !== $fileDestination) {
$shouldDelete = true;
}

// has file changed?

if ($declareNode) {
$nodes = array_merge([$declareNode], [$node]);
} else {
$nodes = [$node];
}

// has file changed?
if ($shouldDelete) {
$this->printNewNodesToFilePath($nodes, $fileDestination);
} else {
$this->printNodesToFilePath($nodes, $fileDestination);
}
}
$this->processNodesWithoutNamespace($nodes, $smartFileInfo, $shouldDelete);
}

if ($shouldDelete) {
$this->removeFile($smartFileInfo);
}
}

/**
* @param Node[] $nodes
*/
private function shouldSkip(SmartFileInfo $smartFileInfo, array $nodes): bool
{
/** @var ClassLike[] $classLikes */
$classLikes = $this->betterNodeFinder->findClassLikes($nodes);

$nonAnonymousClassLikes = array_filter($classLikes, function (ClassLike $classLikeNode): ?Identifier {
return $classLikeNode->name;
});

// only process file with multiple classes || class with non PSR-4 format
if ($nonAnonymousClassLikes === []) {
return true;
}

if (count($nonAnonymousClassLikes) === 1) {
$nonAnonymousClassNode = $nonAnonymousClassLikes[0];
if ((string) $nonAnonymousClassNode->name === $smartFileInfo->getFilename()) {
return true;
}
}

return false;
}

/**
* @param Node[] $nodes
* @return Node[]
Expand Down Expand Up @@ -187,10 +128,12 @@ private function createClassLikeFileDestination(ClassLike $classLike, SmartFileI
* @param Namespace_[] $namespaceNodes
* @param Stmt[] $nodes
*/
private function processNamespaceNodes(SmartFileInfo $smartFileInfo, array $namespaceNodes, array $nodes): bool
{
$shouldDelete = false;

private function processNamespaceNodes(
SmartFileInfo $smartFileInfo,
array $namespaceNodes,
array $nodes,
bool $shouldDeleteFile
): void {
foreach ($namespaceNodes as $namespaceNode) {
$newStmtsSet = $this->removeAllOtherNamespaces($nodes, $namespaceNode);

Expand All @@ -201,7 +144,6 @@ private function processNamespaceNodes(SmartFileInfo $smartFileInfo, array $name

/** @var ClassLike[] $classLikes */
$classLikes = $this->betterNodeFinder->findClassLikes($nodes);

if (count($classLikes) <= 1) {
continue;
}
Expand All @@ -212,20 +154,74 @@ private function processNamespaceNodes(SmartFileInfo $smartFileInfo, array $name

$fileDestination = $this->createClassLikeFileDestination($classLikeNode, $smartFileInfo);

if ($smartFileInfo->getRealPath() !== $fileDestination) {
$shouldDelete = true;
}

// has file changed?
if ($shouldDelete) {
if ($shouldDeleteFile) {
$this->printNewNodesToFilePath($newStmtsSet, $fileDestination);
} else {
$this->printNodesToFilePath($newStmtsSet, $fileDestination);
}
}
}
}
}

return $shouldDelete;
/**
* @param Stmt[] $nodes
*/
private function shouldDeleteFileInfo(SmartFileInfo $smartFileInfo, array $nodes): bool
{
$classLikes = $this->betterNodeFinder->findClassLikes($nodes);
foreach ($classLikes as $classLike) {
$className = $this->getName($classLike);
if ($className === null) {
continue;
}

$classShortName = $this->classNaming->getShortName($className);
if ($smartFileInfo->getBasenameWithoutSuffix() === $classShortName) {
return false;
}
}

return true;
}

/**
* @param Stmt[] $nodes
*/
private function processNodesWithoutNamespace(array $nodes, SmartFileInfo $smartFileInfo, bool $shouldDelete): void
{
// process only files with 2 classes and more
$classes = $this->betterNodeFinder->findClassLikes($nodes);

if (count($classes) <= 1) {
return;
}

$declareNode = null;
foreach ($nodes as $node) {
if ($node instanceof Declare_) {
$declareNode = $node;
}

if (! $node instanceof Class_ || $node->isAnonymous()) {
continue;
}

$fileDestination = $this->createClassLikeFileDestination($node, $smartFileInfo);

if ($declareNode) {
$nodes = array_merge([$declareNode], [$node]);
} else {
$nodes = [$node];
}

// has file changed?
if ($shouldDelete) {
$this->printNewNodesToFilePath($nodes, $fileDestination);
} else {
$this->printNodesToFilePath($nodes, $fileDestination);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);


declare (strict_types=1);
namespace NettePostfixedToUniqueAutoload\Utils;


/**
* The exception that indicates invalid image file.
*/
class UnknownImageFileException extends RegexpException
class UnknownImageFileException extends \NettePostfixedToUniqueAutoload\Utils\RegexpException
{
}
Loading