[Order] Make OrderPrivateMethodsByUseRector process file in one run u… (#3889)

* [Order] Make OrderPrivateMethodsByUseRector process file in one run using while loop

* [Order] Enable rule in CI

* [rector] [Order] Enable rule in CI

* [cs] [Order] Enable rule in CI

* [Order] Use constant for number of max attempts and throw exception when exceeded.

* [rector] [Order] Use constant for number of max attempts and throw exception when exceeded.

* [cs] [Order] Use constant for number of max attempts and throw exception when exceeded.

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
dobryy 2020-08-05 22:45:36 +02:00 committed by GitHub
parent c7731e63e3
commit 411d76f9a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
109 changed files with 2495 additions and 2262 deletions

View File

@ -142,13 +142,6 @@ final class ComposerJsonManipulator
return $json;
}
private function readRemoteFileToJson(string $jsonFilePath): array
{
$jsonFileContent = NetteFileSystem::read($jsonFilePath);
return (array) Json::decode($jsonFileContent, Json::FORCE_ARRAY);
}
private function allowDevDependnecies(array $json): array
{
$json['minimum-stability'] = 'dev';
@ -156,4 +149,11 @@ final class ComposerJsonManipulator
return $json;
}
private function readRemoteFileToJson(string $jsonFilePath): array
{
$jsonFileContent = NetteFileSystem::read($jsonFilePath);
return (array) Json::decode($jsonFileContent, Json::FORCE_ARRAY);
}
}

View File

@ -162,6 +162,17 @@ final class CompileCommand extends Command
return ShellCode::SUCCESS;
}
private function downgradePHPStanCodeToPHP71(OutputInterface $output): void
{
// downgrade phpstan-src code from PHP 7.4 to PHP 7.1, see https://github.com/phpstan/phpstan-src/pull/202/files
$this->fixRequirePath();
$process = new Process(['php', 'vendor/phpstan/phpstan-src/bin/transform-source.php'], $this->buildDir);
$process->mustRun(static function (string $type, string $buffer) use ($output): void {
$output->write($buffer);
});
}
private function restoreDependenciesLocallyIfNotCi(OutputInterface $output): void
{
if ($this->ciDetector->isCiDetected()) {
@ -174,17 +185,6 @@ final class CompileCommand extends Command
});
}
private function downgradePHPStanCodeToPHP71(OutputInterface $output): void
{
// downgrade phpstan-src code from PHP 7.4 to PHP 7.1, see https://github.com/phpstan/phpstan-src/pull/202/files
$this->fixRequirePath();
$process = new Process(['php', 'vendor/phpstan/phpstan-src/bin/transform-source.php'], $this->buildDir);
$process->mustRun(static function (string $type, string $buffer) use ($output): void {
$output->write($buffer);
});
}
private function fixRequirePath(): void
{
// fix require path first

View File

@ -156,28 +156,17 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
);
}
/**
* @param PhpDocTagValueNode[] $tagValueNodes
*/
private function printTagValueNodesSeparatedByComma(array $tagValueNodes): string
private function shouldPrintEmptyBrackets(): bool
{
if ($tagValueNodes === []) {
return '';
// @todo decouple
if ($this->tagValueNodeConfiguration->getOriginalContent() !== null && Strings::endsWith(
$this->tagValueNodeConfiguration->getOriginalContent(),
'()'
)) {
return true;
}
$itemsAsStrings = [];
foreach ($tagValueNodes as $tagValueNode) {
$item = '';
if ($tagValueNode instanceof TagAwareNodeInterface) {
$item .= $tagValueNode->getTag();
}
$item .= (string) $tagValueNode;
$itemsAsStrings[] = $item;
}
return implode(', ', $itemsAsStrings);
return $this->tagValueNodeConfiguration->hasOpeningBracket() && $this->tagValueNodeConfiguration->hasClosingBracket();
}
private function correctArraySingleItemPrint(array $value, string $arrayItemAsString): string
@ -205,16 +194,27 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
return $nakedItem;
}
private function shouldPrintEmptyBrackets(): bool
/**
* @param PhpDocTagValueNode[] $tagValueNodes
*/
private function printTagValueNodesSeparatedByComma(array $tagValueNodes): string
{
// @todo decouple
if ($this->tagValueNodeConfiguration->getOriginalContent() !== null && Strings::endsWith(
$this->tagValueNodeConfiguration->getOriginalContent(),
'()'
)) {
return true;
if ($tagValueNodes === []) {
return '';
}
return $this->tagValueNodeConfiguration->hasOpeningBracket() && $this->tagValueNodeConfiguration->hasClosingBracket();
$itemsAsStrings = [];
foreach ($tagValueNodes as $tagValueNode) {
$item = '';
if ($tagValueNode instanceof TagAwareNodeInterface) {
$item .= $tagValueNode->getTag();
}
$item .= (string) $tagValueNode;
$itemsAsStrings[] = $item;
}
return implode(', ', $itemsAsStrings);
}
}

View File

@ -215,6 +215,19 @@ final class BetterPhpDocParser extends PhpDocParser
return $this->attributeAwareNodeFactory->createFromNode($tagValueNode, $docContent);
}
/**
* @param PhpDocNodeFactoryInterface[] $phpDocNodeFactories
*/
private function setPhpDocNodeFactories(array $phpDocNodeFactories): void
{
foreach ($phpDocNodeFactories as $phpDocNodeFactory) {
$classes = $this->resolvePhpDocNodeFactoryClasses($phpDocNodeFactory);
foreach ($classes as $class) {
$this->phpDocNodeFactories[$class] = $phpDocNodeFactory;
}
}
}
private function parseChildAndStoreItsPositions(TokenIterator $tokenIterator): Node
{
$originalTokenIterator = clone $tokenIterator;
@ -281,6 +294,37 @@ final class BetterPhpDocParser extends PhpDocParser
return $tag;
}
private function matchTagToPhpDocNodeFactory(string $tag): ?PhpDocNodeFactoryInterface
{
$currentPhpNode = $this->currentNodeProvider->getNode();
if ($currentPhpNode === null) {
throw new ShouldNotHappenException();
}
$fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName(
$tag,
$currentPhpNode
);
return $this->phpDocNodeFactories[$fullyQualifiedAnnotationClass] ?? null;
}
/**
* @return string[]
*/
private function resolvePhpDocNodeFactoryClasses(PhpDocNodeFactoryInterface $phpDocNodeFactory): array
{
if ($phpDocNodeFactory instanceof SpecificPhpDocNodeFactoryInterface) {
return $phpDocNodeFactory->getClasses();
}
if ($phpDocNodeFactory instanceof GenericPhpDocNodeFactoryInterface) {
return $phpDocNodeFactory->getTagValueNodeClassesToAnnotationClasses();
}
throw new ShouldNotHappenException();
}
private function getTokenIteratorIndex(TokenIterator $tokenIterator): int
{
return (int) $this->privatesAccessor->getPrivateProperty($tokenIterator, 'index');
@ -344,48 +388,4 @@ final class BetterPhpDocParser extends PhpDocParser
return $tokenEnd;
}
private function matchTagToPhpDocNodeFactory(string $tag): ?PhpDocNodeFactoryInterface
{
$currentPhpNode = $this->currentNodeProvider->getNode();
if ($currentPhpNode === null) {
throw new ShouldNotHappenException();
}
$fullyQualifiedAnnotationClass = $this->classAnnotationMatcher->resolveTagFullyQualifiedName(
$tag,
$currentPhpNode
);
return $this->phpDocNodeFactories[$fullyQualifiedAnnotationClass] ?? null;
}
/**
* @param PhpDocNodeFactoryInterface[] $phpDocNodeFactories
*/
private function setPhpDocNodeFactories(array $phpDocNodeFactories): void
{
foreach ($phpDocNodeFactories as $phpDocNodeFactory) {
$classes = $this->resolvePhpDocNodeFactoryClasses($phpDocNodeFactory);
foreach ($classes as $class) {
$this->phpDocNodeFactories[$class] = $phpDocNodeFactory;
}
}
}
/**
* @return string[]
*/
private function resolvePhpDocNodeFactoryClasses(PhpDocNodeFactoryInterface $phpDocNodeFactory): array
{
if ($phpDocNodeFactory instanceof SpecificPhpDocNodeFactoryInterface) {
return $phpDocNodeFactory->getClasses();
}
if ($phpDocNodeFactory instanceof GenericPhpDocNodeFactoryInterface) {
return $phpDocNodeFactory->getTagValueNodeClassesToAnnotationClasses();
}
throw new ShouldNotHappenException();
}
}

View File

@ -38,6 +38,24 @@ final class ClassAnnotationMatcher
return $fullyQualifiedClass;
}
private function resolveFullyQualifiedClass(?array $useNodes, Node $node, string $tag): string
{
if ($useNodes === null) {
/** @var string|null $namespace */
$namespace = $node->getAttribute(AttributeKey::NAMESPACE_NAME);
if ($namespace !== null) {
$namespacedTag = $namespace . '\\' . $tag;
if (class_exists($namespacedTag)) {
return $namespacedTag;
}
}
return $tag;
}
return $this->matchFullAnnotationClassWithUses($tag, $useNodes) ?? $tag;
}
/**
* @param Use_[] $uses
*/
@ -77,22 +95,4 @@ final class ClassAnnotationMatcher
return $useUse->name . '\\' . $unaliasedShortClass;
}
private function resolveFullyQualifiedClass(?array $useNodes, Node $node, string $tag): string
{
if ($useNodes === null) {
/** @var string|null $namespace */
$namespace = $node->getAttribute(AttributeKey::NAMESPACE_NAME);
if ($namespace !== null) {
$namespacedTag = $namespace . '\\' . $tag;
if (class_exists($namespacedTag)) {
return $namespacedTag;
}
}
return $tag;
}
return $this->matchFullAnnotationClassWithUses($tag, $useNodes) ?? $tag;
}
}

View File

@ -59,6 +59,15 @@ final class TagValueNodeConfigurationFactory
);
}
private function resolveSilentKey(PhpDocTagValueNode $phpDocTagValueNode): ?string
{
if ($phpDocTagValueNode instanceof SilentKeyNodeInterface) {
return $phpDocTagValueNode->getSilentKey();
}
return null;
}
private function isKeyQuoted(string $originalContent, string $key, ?string $silentKey): bool
{
$escapedKey = preg_quote($key, '#');
@ -74,17 +83,6 @@ final class TagValueNodeConfigurationFactory
return (bool) Strings::match($originalContent, $quotedArrayPattern);
}
private function createQuotedKeyPattern(?string $silentKey, string $key, string $escapedKey): string
{
if ($silentKey === $key) {
// @see https://regex101.com/r/VgvK8C/4/
return sprintf('#(%s=")|\("#', $escapedKey);
}
// @see https://regex101.com/r/VgvK8C/3/
return sprintf('#%s="#', $escapedKey);
}
/**
* Before:
* (options={"key":"value"})
@ -114,12 +112,14 @@ final class TagValueNodeConfigurationFactory
return ':';
}
private function resolveSilentKey(PhpDocTagValueNode $phpDocTagValueNode): ?string
private function createQuotedKeyPattern(?string $silentKey, string $key, string $escapedKey): string
{
if ($phpDocTagValueNode instanceof SilentKeyNodeInterface) {
return $phpDocTagValueNode->getSilentKey();
if ($silentKey === $key) {
// @see https://regex101.com/r/VgvK8C/4/
return sprintf('#(%s=")|\("#', $escapedKey);
}
return null;
// @see https://regex101.com/r/VgvK8C/3/
return sprintf('#%s="#', $escapedKey);
}
}

View File

@ -44,14 +44,6 @@ final class FileHashComputer
));
}
/**
* @param mixed[] $array
*/
private function arrayToHash(array $array): string
{
return md5(serialize($array));
}
private function createFileLoader(SmartFileInfo $fileInfo, ContainerBuilder $containerBuilder): LoaderInterface
{
$fileLocator = new FileLocator([$fileInfo->getPath()]);
@ -68,4 +60,12 @@ final class FileHashComputer
return $loader;
}
/**
* @param mixed[] $array
*/
private function arrayToHash(array $array): string
{
return md5(serialize($array));
}
}

View File

@ -55,41 +55,6 @@ final class MarkdownDumpRectorsOutputFormatter
}
}
/**
* @param RectorInterface[] $rectors
* @return RectorInterface[][]
*/
private function groupRectorsByPackage(array $rectors): array
{
$rectorsByPackage = [];
foreach ($rectors as $rector) {
$rectorClass = get_class($rector);
$package = $this->rectorMetadataResolver->resolvePackageFromRectorClass($rectorClass);
$rectorsByPackage[$package][] = $rector;
}
// sort groups by name to make them more readable
ksort($rectorsByPackage);
return $rectorsByPackage;
}
/**
* @param RectorInterface[][] $rectorsByGroup
*/
private function printGroupsMenu(array $rectorsByGroup): void
{
foreach ($rectorsByGroup as $group => $rectors) {
$escapedGroup = str_replace('\\', '', $group);
$escapedGroup = Strings::webalize($escapedGroup, '_');
$message = sprintf('- [%s](#%s) (%d)', $group, $escapedGroup, count($rectors));
$this->symfonyStyle->writeln($message);
}
$this->symfonyStyle->newLine();
}
/**
* @param RectorInterface[] $rectors
*/
@ -130,4 +95,39 @@ final class MarkdownDumpRectorsOutputFormatter
}
}
}
/**
* @param RectorInterface[] $rectors
* @return RectorInterface[][]
*/
private function groupRectorsByPackage(array $rectors): array
{
$rectorsByPackage = [];
foreach ($rectors as $rector) {
$rectorClass = get_class($rector);
$package = $this->rectorMetadataResolver->resolvePackageFromRectorClass($rectorClass);
$rectorsByPackage[$package][] = $rector;
}
// sort groups by name to make them more readable
ksort($rectorsByPackage);
return $rectorsByPackage;
}
/**
* @param RectorInterface[][] $rectorsByGroup
*/
private function printGroupsMenu(array $rectorsByGroup): void
{
foreach ($rectorsByGroup as $group => $rectors) {
$escapedGroup = str_replace('\\', '', $group);
$escapedGroup = Strings::webalize($escapedGroup, '_');
$message = sprintf('- [%s](#%s) (%d)', $group, $escapedGroup, count($rectors));
$this->symfonyStyle->writeln($message);
}
$this->symfonyStyle->newLine();
}
}

View File

@ -122,12 +122,18 @@ final class RectorPrinter
return null;
}
private function getClassRelativePath(string $className): string
private function ensureRectorDefinitionExists(RectorDefinition $rectorDefinition, RectorInterface $rector): void
{
$rectorReflectionClass = new ReflectionClass($className);
$rectorSmartFileInfo = new SmartFileInfo($rectorReflectionClass->getFileName());
if ($rectorDefinition->getDescription() !== '') {
return;
}
return $rectorSmartFileInfo->getRelativeFilePathFromCwd();
$message = sprintf(
'Rector "%s" is missing description. Complete it in "%s()" method.',
get_class($rector),
'getDefinition'
);
throw new ShouldNotHappenException($message);
}
private function ensureCodeSampleExists(RectorDefinition $rectorDefinition, RectorInterface $rector): void
@ -143,17 +149,11 @@ final class RectorPrinter
));
}
private function ensureRectorDefinitionExists(RectorDefinition $rectorDefinition, RectorInterface $rector): void
private function getClassRelativePath(string $className): string
{
if ($rectorDefinition->getDescription() !== '') {
return;
}
$rectorReflectionClass = new ReflectionClass($className);
$rectorSmartFileInfo = new SmartFileInfo($rectorReflectionClass->getFileName());
$message = sprintf(
'Rector "%s" is missing description. Complete it in "%s()" method.',
get_class($rector),
'getDefinition'
);
throw new ShouldNotHappenException($message);
return $rectorSmartFileInfo->getRelativeFilePathFromCwd();
}
}

View File

@ -185,22 +185,6 @@ final class NodeNameResolver
return false;
}
/**
* @param Interface_|Trait_ $classLike
*/
private function resolveNamespacedNameAwareNode(ClassLike $classLike): ?string
{
if (property_exists($classLike, 'namespacedName')) {
return $classLike->namespacedName->toString();
}
if ($classLike->name === null) {
return null;
}
return $this->getName($classLike->name);
}
/**
* @param MethodCall|StaticCall $node
*/
@ -238,6 +222,22 @@ final class NodeNameResolver
throw new ShouldNotHappenException($message);
}
/**
* @param Interface_|Trait_ $classLike
*/
private function resolveNamespacedNameAwareNode(ClassLike $classLike): ?string
{
if (property_exists($classLike, 'namespacedName')) {
return $classLike->namespacedName->toString();
}
if ($classLike->name === null) {
return null;
}
return $this->getName($classLike->name);
}
private function matchRectorBacktraceCall(array $backtrace): ?array
{
foreach ($backtrace as $singleTrace) {

View File

@ -211,6 +211,25 @@ final class PHPStanNodeScopeResolver
}
}
/**
* Remove comments, to enable scope resolving only from code, not docblocks
*
* @param Node[] $nodes
*/
private function removeCommentsFromNodes(array $nodes): void
{
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new class() extends NodeVisitorAbstract {
public function enterNode(Node $node): ?Node
{
$node->setAttribute('comments', null);
return $node;
}
});
$nodeTraverser->traverse($nodes);
}
/**
* @param string[] $dependentFiles
*/
@ -259,23 +278,4 @@ final class PHPStanNodeScopeResolver
$this->symfonyStyle->listing($dependentFiles);
}
}
/**
* Remove comments, to enable scope resolving only from code, not docblocks
*
* @param Node[] $nodes
*/
private function removeCommentsFromNodes(array $nodes): void
{
$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new class() extends NodeVisitorAbstract {
public function enterNode(Node $node): ?Node
{
$node->setAttribute('comments', null);
return $node;
}
});
$nodeTraverser->traverse($nodes);
}
}

View File

@ -170,6 +170,13 @@ final class DocBlockManipulator
return true;
}
private function inlineDocContent(string $docContent): string
{
$docContent = Strings::replace($docContent, "#\n \* #", ' ');
return Strings::replace($docContent, "#\n \*\/$#", ' */');
}
/**
* add // comments to phpdoc (only has /**
*/
@ -196,11 +203,4 @@ final class DocBlockManipulator
{
return Strings::replace($content, '#(\s|\*)+#');
}
private function inlineDocContent(string $docContent): string
{
$docContent = Strings::replace($docContent, "#\n \* #", ' ');
return Strings::replace($docContent, "#\n \*\/$#", ' */');
}
}

View File

@ -111,17 +111,6 @@ final class ConditionResolver
return new VersionCompareCondition($firstVersion, $secondVersion, $versionCompareSign);
}
private function resolveArgumentValue(FuncCall $funcCall, int $argumentPosition): ?string
{
/** @var string|null $version */
$version = $this->valueResolver->getValue($funcCall->args[$argumentPosition]->value);
if ($version === 'PHP_VERSION') {
return $this->phpVersionProvider->provide();
}
return $version;
}
private function resolveFuncCall(
FuncCall $funcCall,
Expr $expr,
@ -136,4 +125,15 @@ final class ConditionResolver
return new BinaryToVersionCompareCondition($versionCompareCondition, $binaryClass, $expectedValue);
}
private function resolveArgumentValue(FuncCall $funcCall, int $argumentPosition): ?string
{
/** @var string|null $version */
$version = $this->valueResolver->getValue($funcCall->args[$argumentPosition]->value);
if ($version === 'PHP_VERSION') {
return $this->phpVersionProvider->provide();
}
return $version;
}
}

View File

@ -143,6 +143,23 @@ final class CreateCommand extends Command
return ShellCode::SUCCESS;
}
/**
* @param string[] $generatedFilePaths
*/
private function resolveTestCaseDirectoryPath(array $generatedFilePaths): string
{
foreach ($generatedFilePaths as $generatedFilePath) {
if (! Strings::endsWith($generatedFilePath, 'Test.php')) {
continue;
}
$generatedFileInfo = new SmartFileInfo($generatedFilePath);
return dirname($generatedFileInfo->getRelativeFilePathFromCwd());
}
throw new ShouldNotHappenException();
}
/**
* @param string[] $generatedFilePaths
*/
@ -163,21 +180,4 @@ final class CreateCommand extends Command
$this->symfonyStyle->success($message);
}
/**
* @param string[] $generatedFilePaths
*/
private function resolveTestCaseDirectoryPath(array $generatedFilePaths): string
{
foreach ($generatedFilePaths as $generatedFilePath) {
if (! Strings::endsWith($generatedFilePath, 'Test.php')) {
continue;
}
$generatedFileInfo = new SmartFileInfo($generatedFilePath);
return dirname($generatedFileInfo->getRelativeFilePathFromCwd());
}
throw new ShouldNotHappenException();
}
}

View File

@ -67,20 +67,6 @@ final class ConfigurationFactory
return (string) Strings::after($nodeTypes[0], '\\', -1);
}
private function normalizeCode(string $code): string
{
if (Strings::startsWith($code, '<?php')) {
$code = ltrim($code, '<?php');
}
return trim($code);
}
private function isPhpSnippet(string $code): bool
{
return Strings::startsWith($code, '<?php');
}
private function resolveExtraFileContent(array $rectorRecipe): ?string
{
return isset($rectorRecipe[RecipeOption::EXTRA_FILE_CONTENT]) ? $this->normalizeCode(
@ -96,4 +82,18 @@ final class ConfigurationFactory
return null;
}
private function normalizeCode(string $code): string
{
if (Strings::startsWith($code, '<?php')) {
$code = ltrim($code, '<?php');
}
return trim($code);
}
private function isPhpSnippet(string $code): bool
{
return Strings::startsWith($code, '<?php');
}
}

View File

@ -53,16 +53,6 @@ final class TemplateFinder
return $this->finderSanitizer->sanitize($filePaths);
}
private function resolveFixtureFilePath(bool $isPhpSnippet): string
{
if ($isPhpSnippet) {
return __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/Fixture/fixture.php.inc';
}
// is html snippet
return __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/Fixture/html_fixture.php.inc';
}
/**
* @param string[] $filePaths
* @return string[]
@ -95,6 +85,16 @@ final class TemplateFinder
return $filePaths;
}
private function resolveFixtureFilePath(bool $isPhpSnippet): string
{
if ($isPhpSnippet) {
return __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/Fixture/fixture.php.inc';
}
// is html snippet
return __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/Fixture/html_fixture.php.inc';
}
/**
* @param string[] $filePaths
*/

View File

@ -127,6 +127,17 @@ final class ConfigurationNodeFactory
return $classMethod;
}
/**
* So types are PHP 7.2 compatible
*/
private function lowerPhpVersion(): void
{
$this->parameterProvider->changeParameter(
Option::PHP_VERSION_FEATURES,
PhpVersionFeature::BEFORE_TYPED_PROPERTIES
);
}
private function createConstantInConfigurationCoalesce(
string $constantName,
Variable $configurationVariable
@ -138,15 +149,4 @@ final class ConfigurationNodeFactory
return new Coalesce($arrayDimFetch, $emptyArray);
}
/**
* So types are PHP 7.2 compatible
*/
private function lowerPhpVersion(): void
{
$this->parameterProvider->changeParameter(
Option::PHP_VERSION_FEATURES,
PhpVersionFeature::BEFORE_TYPED_PROPERTIES
);
}
}

View File

@ -153,17 +153,6 @@ final class TemplateVariablesFactory
return rtrim($sourceAsString);
}
private function createNodeTypePhp(Configuration $configuration): string
{
$referencingClassConsts = [];
foreach ($configuration->getNodeTypes() as $nodeType) {
$referencingClassConsts[] = $this->nodeFactory->createClassConstReference($nodeType);
}
$array = $this->nodeFactory->createArray($referencingClassConsts);
return $this->betterStandardPrinter->print($array);
}
/**
* @param mixed[] $configuration
*/
@ -193,6 +182,15 @@ final class TemplateVariablesFactory
return $this->betterStandardPrinter->print($properties);
}
/**
* @param array<string, mixed> $ruleConfiguration
*/
private function createConfigurationConstants(array $ruleConfiguration): string
{
$configurationConstants = $this->configurationNodeFactory->createConfigurationConstants($ruleConfiguration);
return $this->betterStandardPrinter->print($configurationConstants);
}
/**
* @param array<string, mixed> $ruleConfiguration
*/
@ -202,12 +200,14 @@ final class TemplateVariablesFactory
return $this->betterStandardPrinter->print($classMethod);
}
/**
* @param array<string, mixed> $ruleConfiguration
*/
private function createConfigurationConstants(array $ruleConfiguration): string
private function createNodeTypePhp(Configuration $configuration): string
{
$configurationConstants = $this->configurationNodeFactory->createConfigurationConstants($ruleConfiguration);
return $this->betterStandardPrinter->print($configurationConstants);
$referencingClassConsts = [];
foreach ($configuration->getNodeTypes() as $nodeType) {
$referencingClassConsts[] = $this->nodeFactory->createClassConstReference($nodeType);
}
$array = $this->nodeFactory->createArray($referencingClassConsts);
return $this->betterStandardPrinter->print($array);
}
}

View File

@ -7,7 +7,6 @@ use Rector\CodingStyle\Rector\ClassMethod\ReturnArrayClassMethodToYieldRector;
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
use Rector\Core\Configuration\Option;
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector;
use Rector\Order\Rector\Class_\OrderPrivateMethodsByUseRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Set\ValueObject\SetList;
use Rector\SymfonyPhpConfig\Rector\Closure\AddEmptyLineBetweenCallsInPhpConfigRector;
@ -67,7 +66,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
SplitStringClassConstantToClassConstFetchRector::class,
// false positives on constants used in rector-ci.php
RemoveUnusedClassConstantRector::class,
OrderPrivateMethodsByUseRector::class,
]);
};

View File

@ -112,6 +112,16 @@ PHP
}
}
private function isLocatedInExpectedLocation(
string $groupName,
string $suffixPattern,
SmartFileInfo $smartFileInfo
): bool {
$expectedLocationFilePattern = $this->createExpectedFileLocationPattern($groupName, $suffixPattern);
return (bool) Strings::match($smartFileInfo->getRealPath(), $expectedLocationFilePattern);
}
/**
* @param Node[] $nodes
*/
@ -133,14 +143,4 @@ PHP
{
return sprintf('#\/%s\/.+%s#', preg_quote($groupName, '#'), preg_quote($suffixPattern, '#'));
}
private function isLocatedInExpectedLocation(
string $groupName,
string $suffixPattern,
SmartFileInfo $smartFileInfo
): bool {
$expectedLocationFilePattern = $this->createExpectedFileLocationPattern($groupName, $suffixPattern);
return (bool) Strings::match($smartFileInfo->getRealPath(), $expectedLocationFilePattern);
}
}

View File

@ -173,17 +173,6 @@ CODE_SAMPLE
return $this->classAnalyzer->isValueObjectClass($class);
}
private function isKnownServiceType(string $className): bool
{
foreach (self::COMMON_SERVICE_SUFFIXES as $commonServiceSuffix) {
if (Strings::endsWith($className, $commonServiceSuffix)) {
return true;
}
}
return false;
}
private function isSuffixMatch(Class_ $class): bool
{
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
@ -197,4 +186,15 @@ CODE_SAMPLE
return false;
}
private function isKnownServiceType(string $className): bool
{
foreach (self::COMMON_SERVICE_SUFFIXES as $commonServiceSuffix) {
if (Strings::endsWith($className, $commonServiceSuffix)) {
return true;
}
}
return false;
}
}

View File

@ -225,6 +225,21 @@ PHP
return $this->isName($parentParentNode, $functionName);
}
private function popFirstObjectType(Type $type): Type
{
if ($type instanceof UnionType) {
foreach ($type->getTypes() as $unionedType) {
if (! $unionedType instanceof ObjectType) {
continue;
}
return $unionedType;
}
}
return $type;
}
/**
* @param Param[] $params
* @return Param[]
@ -268,19 +283,4 @@ PHP
}
return false;
}
private function popFirstObjectType(Type $type): Type
{
if ($type instanceof UnionType) {
foreach ($type->getTypes() as $unionedType) {
if (! $unionedType instanceof ObjectType) {
continue;
}
return $unionedType;
}
}
return $type;
}
}

View File

@ -86,15 +86,6 @@ PHP
return $node;
}
private function removeForeachValueAndUseArrayKeys(Foreach_ $foreach): void
{
// remove key value
$foreach->valueVar = $foreach->keyVar;
$foreach->keyVar = null;
$foreach->expr = $this->createFuncCall('array_keys', [$foreach->expr]);
}
private function refactorArrayForeachValue(Array_ $array, Foreach_ $foreach): Array_
{
foreach ($array->items as $key => $arrayItem) {
@ -119,4 +110,13 @@ PHP
return $this->areNodesEqual($node, $variable);
});
}
private function removeForeachValueAndUseArrayKeys(Foreach_ $foreach): void
{
// remove key value
$foreach->valueVar = $foreach->keyVar;
$foreach->keyVar = null;
$foreach->expr = $this->createFuncCall('array_keys', [$foreach->expr]);
}
}

View File

@ -187,6 +187,24 @@ PHP
return new Return_($this->boolCastOrNullCompareIfNeeded(new BooleanNot($if->cond)));
}
/**
* Matches: "else if"
*/
private function isElseSeparatedThenIf(If_ $if): bool
{
if ($if->else === null) {
return false;
}
if (count($if->else->stmts) !== 1) {
return false;
}
$onlyStmt = $if->else->stmts[0];
return $onlyStmt instanceof If_;
}
private function isIfWithSingleReturnExpr(If_ $if): bool
{
if (count($if->stmts) !== 1) {
@ -240,22 +258,4 @@ PHP
return ! $expr instanceof BinaryOp;
}
/**
* Matches: "else if"
*/
private function isElseSeparatedThenIf(If_ $if): bool
{
if ($if->else === null) {
return false;
}
if (count($if->else->stmts) !== 1) {
return false;
}
$onlyStmt = $if->else->stmts[0];
return $onlyStmt instanceof If_;
}
}

View File

@ -101,6 +101,22 @@ final class ShortNameResolver
return array_unique($shortClassLikeNames);
}
private function getNodeRealPath(Node $node): ?string
{
/** @var SmartFileInfo|null $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if ($fileInfo !== null) {
return $fileInfo->getRealPath();
}
$currentFileInfo = $this->currentFileInfoProvider->getSmartFileInfo();
if ($currentFileInfo !== null) {
return $currentFileInfo->getRealPath();
}
return null;
}
/**
* @param Node[] $stmts
* @return string[]
@ -140,22 +156,6 @@ final class ShortNameResolver
return array_merge($shortNames, $docBlockShortNames);
}
private function getNodeRealPath(Node $node): ?string
{
/** @var SmartFileInfo|null $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if ($fileInfo !== null) {
return $fileInfo->getRealPath();
}
$currentFileInfo = $this->currentFileInfoProvider->getSmartFileInfo();
if ($currentFileInfo !== null) {
return $currentFileInfo->getRealPath();
}
return null;
}
/**
* @param Node[] $stmts
* @return string[]

View File

@ -105,13 +105,21 @@ PHP
$this->previousPreviousStmtVariableName = null;
}
/**
* @param Assign|MethodCall $node
*/
private function shouldSkipLeftVariable(Node $node): bool
private function resolveCurrentStmtVariableName(Node $node): ?string
{
// local method call
return $this->isVariableName($node->var, 'this');
$node = $this->unwrapExpression($node);
if ($node instanceof Assign || $node instanceof MethodCall) {
if ($this->shouldSkipLeftVariable($node)) {
return null;
}
if (! $node->var instanceof MethodCall && ! $node->var instanceof StaticCall) {
return $this->getName($node->var);
}
}
return null;
}
/**
@ -127,6 +135,24 @@ PHP
return ! $this->isPreceededByEmptyLine($node, $key);
}
private function unwrapExpression(Node $node): Node
{
if ($node instanceof Expression) {
return $node->expr;
}
return $node;
}
/**
* @param Assign|MethodCall $node
*/
private function shouldSkipLeftVariable(Node $node): bool
{
// local method call
return $this->isVariableName($node->var, 'this');
}
private function isNewVariableThanBefore(?string $currentStmtVariableName): bool
{
if ($this->previousPreviousStmtVariableName === null) {
@ -162,30 +188,4 @@ PHP
return abs($currentNode->getLine() - $previousNode->getLine()) >= 2;
}
private function unwrapExpression(Node $node): Node
{
if ($node instanceof Expression) {
return $node->expr;
}
return $node;
}
private function resolveCurrentStmtVariableName(Node $node): ?string
{
$node = $this->unwrapExpression($node);
if ($node instanceof Assign || $node instanceof MethodCall) {
if ($this->shouldSkipLeftVariable($node)) {
return null;
}
if (! $node->var instanceof MethodCall && ! $node->var instanceof StaticCall) {
return $this->getName($node->var);
}
}
return null;
}
}

View File

@ -205,6 +205,52 @@ PHP
$useUse->alias = null;
}
private function hasUseAlias(Use_ $use): bool
{
foreach ($use->uses as $useUse) {
if ($useUse->alias !== null) {
return true;
}
}
return false;
}
/**
* @param NameAndParentValueObject[] $usedNameNodes
*/
private function renameNameNode(array $usedNameNodes, string $lastName): void
{
foreach ($usedNameNodes as $nameAndParent) {
$parentNode = $nameAndParent->getParentNode();
$usedName = $nameAndParent->getNameNode();
if ($parentNode instanceof TraitUse) {
$this->renameTraitUse($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Class_) {
$this->renameClass($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Param) {
$this->renameParam($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof New_) {
$this->renameNew($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof ClassMethod) {
$this->renameClassMethod($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Interface_) {
$this->renameInterface($lastName, $parentNode, $usedName);
}
}
}
/**
* @param Name|Identifier $usedNameNode
*/
@ -280,50 +326,4 @@ PHP
}
}
}
/**
* @param NameAndParentValueObject[] $usedNameNodes
*/
private function renameNameNode(array $usedNameNodes, string $lastName): void
{
foreach ($usedNameNodes as $nameAndParent) {
$parentNode = $nameAndParent->getParentNode();
$usedName = $nameAndParent->getNameNode();
if ($parentNode instanceof TraitUse) {
$this->renameTraitUse($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Class_) {
$this->renameClass($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Param) {
$this->renameParam($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof New_) {
$this->renameNew($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof ClassMethod) {
$this->renameClassMethod($lastName, $parentNode, $usedName);
}
if ($parentNode instanceof Interface_) {
$this->renameInterface($lastName, $parentNode, $usedName);
}
}
}
private function hasUseAlias(Use_ $use): bool
{
foreach ($use->uses as $useUse) {
if ($useUse->alias !== null) {
return true;
}
}
return false;
}
}

View File

@ -121,13 +121,6 @@ final class LivingCodeManipulator
);
}
private function processIsset(Isset_ $isset): array
{
return array_merge(...array_map(function (Expr $expr): array {
return $this->keepLivingCodeFromExpr($expr);
}, $isset->vars));
}
private function processBinary(BinaryOp $binaryOp): array
{
return array_merge(
@ -135,4 +128,11 @@ final class LivingCodeManipulator
$this->keepLivingCodeFromExpr($binaryOp->right)
);
}
private function processIsset(Isset_ $isset): array
{
return array_merge(...array_map(function (Expr $expr): array {
return $this->keepLivingCodeFromExpr($expr);
}, $isset->vars));
}
}

View File

@ -212,29 +212,6 @@ PHP
);
}
/**
* @return Param[]
*/
private function resolveUnusedParameters(ClassMethod $classMethod): array
{
$unusedParameters = [];
foreach ((array) $classMethod->params as $i => $param) {
if ($this->classMethodManipulator->isParameterUsedInClassMethod($param, $classMethod)) {
// reset to keep order of removed arguments, if not construtctor - probably autowired
if (! $this->isName($classMethod, MethodName::CONSTRUCT)) {
$unusedParameters = [];
}
continue;
}
$unusedParameters[$i] = $param;
}
return $unusedParameters;
}
private function shouldSkipOpenSourceAbstract(ClassMethod $classMethod, Class_ $class): bool
{
// skip as possible contract for 3rd party
@ -286,4 +263,27 @@ PHP
// can be opened
return $classMethod->isProtected();
}
/**
* @return Param[]
*/
private function resolveUnusedParameters(ClassMethod $classMethod): array
{
$unusedParameters = [];
foreach ((array) $classMethod->params as $i => $param) {
if ($this->classMethodManipulator->isParameterUsedInClassMethod($param, $classMethod)) {
// reset to keep order of removed arguments, if not construtctor - probably autowired
if (! $this->isName($classMethod, MethodName::CONSTRUCT)) {
$unusedParameters = [];
}
continue;
}
$unusedParameters[$i] = $param;
}
return $unusedParameters;
}
}

View File

@ -96,19 +96,6 @@ final class UsedClassPropertyExtractor
return $properties;
}
private function isThisPropertyFetch(Node $node): bool
{
if ($node instanceof MethodCall) {
return false;
}
if ($node instanceof StaticCall) {
return false;
}
return $this->nodeNameResolver->isName($node, 'this');
}
/**
* @param Property[] $classProperties
* @return Property[]
@ -132,4 +119,17 @@ final class UsedClassPropertyExtractor
return $classProperties;
}
private function isThisPropertyFetch(Node $node): bool
{
if ($node instanceof MethodCall) {
return false;
}
if ($node instanceof StaticCall) {
return false;
}
return $this->nodeNameResolver->isName($node, 'this');
}
}

View File

@ -60,6 +60,18 @@ final class CallTypeAnalyzer
return $this->getMethodParameterTypes($callerClassName, $node);
}
/**
* @param StaticCall|MethodCall $node
*/
private function resolveCallerType(Node $node): Type
{
if ($node instanceof MethodCall) {
return $this->nodeTypeResolver->getStaticType($node->var);
}
return $this->nodeTypeResolver->resolve($node->class);
}
/**
* @param MethodCall|StaticCall $node
* @return array<int, Type>
@ -88,16 +100,4 @@ final class CallTypeAnalyzer
return $parameterTypes;
}
/**
* @param StaticCall|MethodCall $node
*/
private function resolveCallerType(Node $node): Type
{
if ($node instanceof MethodCall) {
return $this->nodeTypeResolver->getStaticType($node->var);
}
return $this->nodeTypeResolver->resolve($node->class);
}
}

View File

@ -115,18 +115,6 @@ PHP
$this->oldToNewMethodByType = $configuration[self::OLD_TO_NEW_METHOD_BY_TYPE] ?? [];
}
private function wrapReturnValueToArray(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
if (! $node instanceof Return_) {
return null;
}
$node->expr = $this->createArray([$node->expr]);
return null;
});
}
private function keepOldReturnTypeInDocBlock(ClassMethod $classMethod): void
{
// keep old return type in the docblock
@ -142,4 +130,16 @@ PHP
$phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO);
$phpDocInfo->changeReturnType($arrayType);
}
private function wrapReturnValueToArray(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
if (! $node instanceof Return_) {
return null;
}
$node->expr = $this->createArray([$node->expr]);
return null;
});
}
}

View File

@ -93,6 +93,24 @@ PHP
$this->newArgumentPositionsByMethodAndClass = $configuration[self::NEW_ARGUMENT_POSITIONS_BY_METHOD_AND_CLASS] ?? [];
}
/**
* @param StaticCall|MethodCall|ClassMethod $node
*/
private function refactorArgumentPositions(array $methodNameAndNewArgumentPositions, Node $node): void
{
foreach ($methodNameAndNewArgumentPositions as $methodName => $newArgumentPositions) {
if (! $this->isMethodStaticCallOrClassMethodName($node, $methodName)) {
continue;
}
if ($node instanceof ClassMethod) {
$this->swapParameters($node, $newArgumentPositions);
} else {
$this->swapArguments($node, $newArgumentPositions);
}
}
}
/**
* @param StaticCall|MethodCall|ClassMethod $node
*/
@ -132,24 +150,6 @@ PHP
}
}
/**
* @param StaticCall|MethodCall|ClassMethod $node
*/
private function refactorArgumentPositions(array $methodNameAndNewArgumentPositions, Node $node): void
{
foreach ($methodNameAndNewArgumentPositions as $methodName => $newArgumentPositions) {
if (! $this->isMethodStaticCallOrClassMethodName($node, $methodName)) {
continue;
}
if ($node instanceof ClassMethod) {
$this->swapParameters($node, $newArgumentPositions);
} else {
$this->swapArguments($node, $newArgumentPositions);
}
}
}
/**
* @param MethodCall|StaticCall $node
* @param int[] $newArgumentPositions

View File

@ -124,24 +124,6 @@ PHP
$this->printNodesWithFileDestination($nodesWithFileDestination);
}
/**
* @param Node[] $nodes
* @return Node[]
*/
private function resolveNodesToPrint(array $nodes, Class_ $class): array
{
/** @var Namespace_|null $namespace */
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace !== null) {
// put class first
$namespace->stmts = array_merge([$class], $namespace->stmts);
return [$namespace];
}
return [$class];
}
/**
* @param Node[] $nodes
* @return Node[]
@ -182,4 +164,22 @@ PHP
return $methodBuilder->getNode();
}
/**
* @param Node[] $nodes
* @return Node[]
*/
private function resolveNodesToPrint(array $nodes, Class_ $class): array
{
/** @var Namespace_|null $namespace */
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace !== null) {
// put class first
$namespace->stmts = array_merge([$class], $namespace->stmts);
return [$namespace];
}
return [$class];
}
}

View File

@ -115,13 +115,6 @@ PHP
return false;
}
private function createInclude(): Include_
{
$filePathConcat = new Concat(new Dir(), new String_($this->autoloadFilePath));
return new Include_($filePathConcat, Include_::TYPE_REQUIRE_ONCE);
}
/**
* Find all includes and see if any match what we want to insert
*/
@ -138,6 +131,13 @@ PHP
return false;
}
private function createInclude(): Include_
{
$filePathConcat = new Concat(new Dir(), new String_($this->autoloadFilePath));
return new Include_($filePathConcat, Include_::TYPE_REQUIRE_ONCE);
}
private function isTopFileInclude(Include_ $include): bool
{
return $this->areNodesEqual($include->expr, $this->createInclude()->expr);

View File

@ -55,16 +55,6 @@ final class ParentClassMethodTypeOverrideGuard
return $parentClassMethod !== null;
}
private function getClassMethodByMethodReflection(MethodReflection $parentClassMethodReflection): ?ClassMethod
{
$methodName = $parentClassMethodReflection->getName();
/** @var string $className */
$className = $parentClassMethodReflection->getDeclaringClass()->getName();
return $this->parsedFunctionLikeNodeCollector->findMethod($className, $methodName);
}
private function getParentClassMethod(ClassMethod $classMethod): ?MethodReflection
{
/** @var string $methodName */
@ -94,4 +84,14 @@ final class ParentClassMethodTypeOverrideGuard
return null;
}
private function getClassMethodByMethodReflection(MethodReflection $parentClassMethodReflection): ?ClassMethod
{
$methodName = $parentClassMethodReflection->getName();
/** @var string $className */
$className = $parentClassMethodReflection->getDeclaringClass()->getName();
return $this->parsedFunctionLikeNodeCollector->findMethod($className, $methodName);
}
}

View File

@ -83,6 +83,21 @@ final class NonFluentChainMethodCallFactory
return $nodesToAdd;
}
private function createAssignExpression(Variable $newVariable, New_ $new): Expression
{
$assign = new Assign($newVariable, $new);
return new Expression($assign);
}
private function isNewNodeNeeded(AssignAndRootExpr $assignAndRootExpr): bool
{
if (! $assignAndRootExpr->getRootExpr() instanceof New_) {
return false;
}
return $assignAndRootExpr->getRootExpr() !== $assignAndRootExpr->getAssignExpr();
}
/**
* @param MethodCall[] $chainMethodCalls
* @return Expr[]
@ -105,19 +120,4 @@ final class NonFluentChainMethodCallFactory
return array_reverse($decoupledMethodCalls);
}
private function isNewNodeNeeded(AssignAndRootExpr $assignAndRootExpr): bool
{
if (! $assignAndRootExpr->getRootExpr() instanceof New_) {
return false;
}
return $assignAndRootExpr->getRootExpr() !== $assignAndRootExpr->getAssignExpr();
}
private function createAssignExpression(Variable $newVariable, New_ $new): Expression
{
$assign = new Assign($newVariable, $new);
return new Expression($assign);
}
}

View File

@ -110,6 +110,18 @@ PHP
return $node;
}
private function isGetterMethodCall(MethodCall $methodCall): bool
{
if ($methodCall->var instanceof MethodCall) {
return false;
}
$methodCallStaticType = $this->getStaticType($methodCall);
$methodCallVarStaticType = $this->getStaticType($methodCall->var);
// getter short call type
return ! $methodCallStaticType->equals($methodCallVarStaticType);
}
/**
* @duplicated
* @param MethodCall|Return_ $node
@ -133,16 +145,4 @@ PHP
$this->removeNode($node);
}
private function isGetterMethodCall(MethodCall $methodCall): bool
{
if ($methodCall->var instanceof MethodCall) {
return false;
}
$methodCallStaticType = $this->getStaticType($methodCall);
$methodCallVarStaticType = $this->getStaticType($methodCall->var);
// getter short call type
return ! $methodCallStaticType->equals($methodCallVarStaticType);
}
}

View File

@ -124,15 +124,25 @@ PHP
return $assignAndRootExpr->getCallerExpr();
}
private function removeParentParent(MethodCall $methodCall): void
private function refactorNew(MethodCall $methodCall, New_ $new): void
{
/** @var Arg $parent */
$parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE);
if (! $this->newFluentChainMethodCallNodeAnalyzer->isNewMethodCallReturningSelf($methodCall)) {
return;
}
/** @var MethodCall $parentParent */
$parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE);
$nodesToAdd = $this->nonFluentChainMethodCallFactory->createFromNewAndRootMethodCall($new, $methodCall);
$this->removeNode($parentParent);
$newVariable = $this->crateVariableFromNew($new);
$nodesToAdd[] = $this->createFluentAsArg($methodCall, $newVariable);
$this->addNodesBeforeNode($nodesToAdd, $methodCall);
$this->removeParentParent($methodCall);
}
private function crateVariableFromNew(New_ $new): Variable
{
$variableName = $this->variableNaming->resolveFromNode($new);
return new Variable($variableName);
}
/**
@ -152,24 +162,14 @@ PHP
return $lastMethodCall;
}
private function crateVariableFromNew(New_ $new): Variable
private function removeParentParent(MethodCall $methodCall): void
{
$variableName = $this->variableNaming->resolveFromNode($new);
return new Variable($variableName);
}
/** @var Arg $parent */
$parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE);
private function refactorNew(MethodCall $methodCall, New_ $new): void
{
if (! $this->newFluentChainMethodCallNodeAnalyzer->isNewMethodCallReturningSelf($methodCall)) {
return;
}
/** @var MethodCall $parentParent */
$parentParent = $parent->getAttribute(AttributeKey::PARENT_NODE);
$nodesToAdd = $this->nonFluentChainMethodCallFactory->createFromNewAndRootMethodCall($new, $methodCall);
$newVariable = $this->crateVariableFromNew($new);
$nodesToAdd[] = $this->createFluentAsArg($methodCall, $newVariable);
$this->addNodesBeforeNode($nodesToAdd, $methodCall);
$this->removeParentParent($methodCall);
$this->removeNode($parentParent);
}
}

View File

@ -95,6 +95,32 @@ PHP
});
}
private function revealMockArguments(ClassMethod $classMethod): void
{
if ($classMethod->stmts === null) {
return;
}
$this->traverseNodesWithCallable($classMethod->stmts, function (Node $node) {
if (! $node instanceof Arg) {
return null;
}
if (! $node->value instanceof Variable) {
return null;
}
/** @var string $variableName */
$variableName = $this->getName($node->value);
if (! isset($this->mockVariableTypesByNames[$variableName])) {
return null;
}
return $this->createMethodCall($node->value, 'reveal');
});
}
private function collectMockVariableName(StaticCall $staticCall): void
{
$parentNode = $staticCall->getAttribute(AttributeKey::PARENT_NODE);
@ -122,30 +148,4 @@ PHP
{
return $this->createLocalMethodCall('prophesize', [$staticCall->args[0]]);
}
private function revealMockArguments(ClassMethod $classMethod): void
{
if ($classMethod->stmts === null) {
return;
}
$this->traverseNodesWithCallable($classMethod->stmts, function (Node $node) {
if (! $node instanceof Arg) {
return null;
}
if (! $node->value instanceof Variable) {
return null;
}
/** @var string $variableName */
$variableName = $this->getName($node->value);
if (! isset($this->mockVariableTypesByNames[$variableName])) {
return null;
}
return $this->createMethodCall($node->value, 'reveal');
});
}
}

View File

@ -90,45 +90,6 @@ PHP
return $node;
}
private function collectMockVariableName(FuncCall $funcCall): void
{
$parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof Assign) {
return;
}
if (! $parentNode->var instanceof Variable) {
return;
}
/** @var Variable $variable */
$variable = $parentNode->var;
/** @var string $variableName */
$variableName = $this->getName($variable);
$type = $funcCall->args[0]->value;
$mockedType = $this->getValue($type);
$this->mockVariableTypesByNames[$variableName] = $mockedType;
}
private function isMethodCallOrPropertyFetchOnMockVariable(Node $node): bool
{
if (! $node instanceof MethodCall && ! $this->isPropertyFetchDisguisedAsMethodCall($node)) {
return false;
}
if (! $node->var instanceof Variable) {
return false;
}
/** @var string $variableName */
$variableName = $this->getName($node->var);
return isset($this->mockVariableTypesByNames[$variableName]);
}
private function replaceMockWithMockerMockAndCollectMockVariableName(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
@ -143,6 +104,26 @@ PHP
});
}
/**
* $mock->getMethod()->once
*
* $mock->getMethod()->once()
*/
private function replaceMethodCallOncePropertyFetch(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
if (! $node instanceof PropertyFetch) {
return null;
}
if (! $this->isNames($node->name, ['once', 'twice'])) {
return null;
}
return new MethodCall($node->var, $node->name);
});
}
private function removeUnusedMethodCalls(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
@ -200,47 +181,70 @@ PHP
}
/**
* $mock->getMethod()->once
*
* $mock->getMethod()->once()
* Order correction for @see replaceMethodCallWithExpects()
*/
private function replaceMethodCallOncePropertyFetch(ClassMethod $classMethod): void
private function switchWithAnyArgsAndOnceTwice(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
if (! $node instanceof PropertyFetch) {
if (! $node instanceof MethodCall) {
return null;
}
if (! $this->isNames($node->name, ['once', 'twice'])) {
return;
}
if (! $node->var instanceof MethodCall) {
return null;
}
return new MethodCall($node->var, $node->name);
/** @var MethodCall $previousMethodCall */
$previousMethodCall = $node->var;
if (! $this->isName($previousMethodCall->name, 'withAnyArgs')) {
return null;
}
[$node->name, $previousMethodCall->name] = [$previousMethodCall->name, $node->name];
});
}
private function isPropertyFetchDisguisedAsMethodCall(Node $node): bool
private function collectMockVariableName(FuncCall $funcCall): void
{
if (! $node instanceof PropertyFetch) {
$parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof Assign) {
return;
}
if (! $parentNode->var instanceof Variable) {
return;
}
/** @var Variable $variable */
$variable = $parentNode->var;
/** @var string $variableName */
$variableName = $this->getName($variable);
$type = $funcCall->args[0]->value;
$mockedType = $this->getValue($type);
$this->mockVariableTypesByNames[$variableName] = $mockedType;
}
private function isMethodCallOrPropertyFetchOnMockVariable(Node $node): bool
{
if (! $node instanceof MethodCall && ! $this->isPropertyFetchDisguisedAsMethodCall($node)) {
return false;
}
if ($node->var instanceof MethodCall) {
if (! $node->var instanceof Variable) {
return false;
}
/** @var string $variableName */
$variableName = $this->getName($node->var);
if (! isset($this->mockVariableTypesByNames[$variableName])) {
return false;
}
$mockVariableType = $this->mockVariableTypesByNames[$variableName];
$propertyName = $this->getName($node->name);
if ($propertyName === null) {
return false;
}
return method_exists($mockVariableType, $propertyName);
return isset($this->mockVariableTypesByNames[$variableName]);
}
/**
@ -270,31 +274,27 @@ PHP
return new MethodCall($expectsMethodCall, 'withAnyArgs');
}
/**
* Order correction for @see replaceMethodCallWithExpects()
*/
private function switchWithAnyArgsAndOnceTwice(ClassMethod $classMethod): void
private function isPropertyFetchDisguisedAsMethodCall(Node $node): bool
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
if (! $node instanceof MethodCall) {
return null;
}
if (! $node instanceof PropertyFetch) {
return false;
}
if (! $this->isNames($node->name, ['once', 'twice'])) {
return;
}
if ($node->var instanceof MethodCall) {
return false;
}
if (! $node->var instanceof MethodCall) {
return null;
}
$variableName = $this->getName($node->var);
if (! isset($this->mockVariableTypesByNames[$variableName])) {
return false;
}
/** @var MethodCall $previousMethodCall */
$previousMethodCall = $node->var;
if (! $this->isName($previousMethodCall->name, 'withAnyArgs')) {
return null;
}
$mockVariableType = $this->mockVariableTypesByNames[$variableName];
$propertyName = $this->getName($node->name);
if ($propertyName === null) {
return false;
}
[$node->name, $previousMethodCall->name] = [$previousMethodCall->name, $node->name];
});
return method_exists($mockVariableType, $propertyName);
}
}

View File

@ -159,6 +159,26 @@ final class BreakingVariableRenameGuard
return $trinaryLogic->maybe();
}
/**
* @param ClassMethod|Function_|Closure $functionLike
*/
private function skipOnConflictOtherVariable(FunctionLike $functionLike, string $newName): bool
{
return $this->betterNodeFinder->hasInstanceOfName((array) $functionLike->stmts, Variable::class, $newName);
}
/**
* @param ClassMethod|Function_|Closure $functionLike
*/
private function isUsedInClosureUsesName(string $expectedName, FunctionLike $functionLike): bool
{
if (! $functionLike instanceof Closure) {
return false;
}
return $this->betterNodeFinder->hasVariableOfName((array) $functionLike->uses, $expectedName);
}
private function isUsedInIfAndOtherBranches(Variable $variable, string $currentVariableName): bool
{
// is in if branches?
@ -194,24 +214,4 @@ final class BreakingVariableRenameGuard
return (bool) Strings::match($currentName, '#[\w+]At$#');
}
/**
* @param ClassMethod|Function_|Closure $functionLike
*/
private function isUsedInClosureUsesName(string $expectedName, FunctionLike $functionLike): bool
{
if (! $functionLike instanceof Closure) {
return false;
}
return $this->betterNodeFinder->hasVariableOfName((array) $functionLike->uses, $expectedName);
}
/**
* @param ClassMethod|Function_|Closure $functionLike
*/
private function skipOnConflictOtherVariable(FunctionLike $functionLike, string $newName): bool
{
return $this->betterNodeFinder->hasInstanceOfName((array) $functionLike->stmts, Variable::class, $newName);
}
}

View File

@ -54,16 +54,6 @@ final class VariableAndCallAssignMatcher
return new VariableAndCallAssign($assign->var, $call, $assign, $variableName, $functionLike);
}
/**
* @return ClassMethod|Function_|Closure|null
*/
private function getFunctionLike(Node $node): ?FunctionLike
{
return $node->getAttribute(AttributeKey::CLOSURE_NODE) ??
$node->getAttribute(AttributeKey::METHOD_NODE) ??
$node->getAttribute(AttributeKey::FUNCTION_NODE);
}
/**
* @return FuncCall|StaticCall|MethodCall|null
*/
@ -83,4 +73,14 @@ final class VariableAndCallAssignMatcher
return null;
}
/**
* @return ClassMethod|Function_|Closure|null
*/
private function getFunctionLike(Node $node): ?FunctionLike
{
return $node->getAttribute(AttributeKey::CLOSURE_NODE) ??
$node->getAttribute(AttributeKey::METHOD_NODE) ??
$node->getAttribute(AttributeKey::FUNCTION_NODE);
}
}

View File

@ -106,6 +106,24 @@ final class PropertyNaming
return lcfirst($camelCaseName);
}
private function getClassName(TypeWithClassName $typeWithClassName): string
{
if ($typeWithClassName instanceof ShortenedObjectType) {
return $typeWithClassName->getFullyQualifiedName();
}
return $typeWithClassName->getClassName();
}
private function resolveShortClassName(string $className): string
{
if (Strings::contains($className, '\\')) {
return Strings::after($className, '\\', -1);
}
return $className;
}
private function removePrefixesAndSuffixes(string $shortClassName): string
{
// is SomeInterface
@ -126,37 +144,6 @@ final class PropertyNaming
return $shortClassName;
}
private function isPrefixedInterface(string $shortClassName): bool
{
if (strlen($shortClassName) <= 3) {
return false;
}
if (! Strings::startsWith($shortClassName, 'I')) {
return false;
}
return ctype_upper($shortClassName[1]) && ctype_lower($shortClassName[2]);
}
private function getClassName(TypeWithClassName $typeWithClassName): string
{
if ($typeWithClassName instanceof ShortenedObjectType) {
return $typeWithClassName->getFullyQualifiedName();
}
return $typeWithClassName->getClassName();
}
private function resolveShortClassName(string $className): string
{
if (Strings::contains($className, '\\')) {
return Strings::after($className, '\\', -1);
}
return $className;
}
private function normalizeUpperCase(string $shortClassName): string
{
// turns $SOMEUppercase => $someUppercase
@ -171,13 +158,16 @@ final class PropertyNaming
return $shortClassName;
}
private function isNumberOrUpper(string $char): bool
/**
* @param ObjectType|string $objectType
*/
private function resolveClassName($objectType): string
{
if (ctype_upper($char)) {
return true;
if ($objectType instanceof ObjectType) {
return $objectType->getClassName();
}
return ctype_digit($char);
return $objectType;
}
private function fqnToShortName(string $fqn): string
@ -214,15 +204,25 @@ final class PropertyNaming
return $shortName;
}
/**
* @param ObjectType|string $objectType
*/
private function resolveClassName($objectType): string
private function isPrefixedInterface(string $shortClassName): bool
{
if ($objectType instanceof ObjectType) {
return $objectType->getClassName();
if (strlen($shortClassName) <= 3) {
return false;
}
return $objectType;
if (! Strings::startsWith($shortClassName, 'I')) {
return false;
}
return ctype_upper($shortClassName[1]) && ctype_lower($shortClassName[2]);
}
private function isNumberOrUpper(string $char): bool
{
if (ctype_upper($char)) {
return true;
}
return ctype_digit($char);
}
}

View File

@ -155,6 +155,28 @@ PHP
return $node;
}
private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string $expectedName): bool
{
if ($this->namingConventionAnalyzer->isCallMatchingVariableName(
$variableAndCallAssign->getCall(),
$variableAndCallAssign->getVariableName(),
$expectedName
)) {
return true;
}
if ($this->isClassTypeWithChildren($variableAndCallAssign->getCall())) {
return true;
}
return $this->breakingVariableRenameGuard->shouldSkipVariable(
$variableAndCallAssign->getVariableName(),
$expectedName,
$variableAndCallAssign->getFunctionLike(),
$variableAndCallAssign->getVariable()
);
}
private function renameVariable(VariableAndCallAssign $variableAndCallAssign, string $expectedName): void
{
$this->varTagValueNodeRenamer->renameAssignVarTagVariableName(
@ -192,26 +214,4 @@ PHP
return $this->familyRelationsAnalyzer->isParentClass($callStaticType->getClassName());
}
private function shouldSkip(VariableAndCallAssign $variableAndCallAssign, string $expectedName): bool
{
if ($this->namingConventionAnalyzer->isCallMatchingVariableName(
$variableAndCallAssign->getCall(),
$variableAndCallAssign->getVariableName(),
$expectedName
)) {
return true;
}
if ($this->isClassTypeWithChildren($variableAndCallAssign->getCall())) {
return true;
}
return $this->breakingVariableRenameGuard->shouldSkipVariable(
$variableAndCallAssign->getVariableName(),
$expectedName,
$variableAndCallAssign->getFunctionLike(),
$variableAndCallAssign->getVariable()
);
}
}

View File

@ -143,6 +143,19 @@ PHP
}
}
private function shouldSkipProperty(
Property $property,
string $oldName,
string $expectedName,
array $conflictingPropertyNames
): bool {
if ($this->isObjectType($property, 'Ramsey\Uuid\UuidInterface')) {
return true;
}
return $this->shouldSkipPropertyRename($property, $oldName, $expectedName, $conflictingPropertyNames);
}
private function renamePropertyFetchesInClass(ClassLike $classLike, string $oldName, string $expectedName): void
{
// 1. replace property fetch rename in whole class
@ -172,17 +185,4 @@ PHP
return $this->breakingVariableRenameGuard->shouldSkipProperty($property, $currentName);
}
private function shouldSkipProperty(
Property $property,
string $oldName,
string $expectedName,
array $conflictingPropertyNames
): bool {
if ($this->isObjectType($property, 'Ramsey\Uuid\UuidInterface')) {
return true;
}
return $this->shouldSkipPropertyRename($property, $oldName, $expectedName, $conflictingPropertyNames);
}
}

View File

@ -83,6 +83,11 @@ final class VariableRenamer
);
}
private function isScopingNode(Node $node): bool
{
return $node instanceof Closure || $node instanceof Function_ || $node instanceof ClassMethod;
}
private function renameVariableIfMatchesName(Variable $variable, string $oldName, string $expectedName): ?Variable
{
if (! $this->nodeNameResolver->isName($variable, $oldName)) {
@ -94,9 +99,4 @@ final class VariableRenamer
return $variable;
}
private function isScopingNode(Node $node): bool
{
return $node instanceof Closure || $node instanceof Function_ || $node instanceof ClassMethod;
}
}

View File

@ -96,15 +96,6 @@ abstract class AbstractArrayDimFetchToAnnotatedControlVariableRector extends Abs
return $parent->expr === $arrayDimFetch;
}
private function createAssignExpression(string $variableName, ArrayDimFetch $arrayDimFetch): Expression
{
$variable = new Variable($variableName);
$assignedArrayDimFetch = clone $arrayDimFetch;
$assign = new Assign($variable, $assignedArrayDimFetch);
return new Expression($assign);
}
private function shouldSkipForAlreadyAddedInCurrentClassMethod(
ArrayDimFetch $arrayDimFetch,
string $variableName
@ -169,6 +160,15 @@ abstract class AbstractArrayDimFetchToAnnotatedControlVariableRector extends Abs
return $currentStatement;
}
private function createAssignExpression(string $variableName, ArrayDimFetch $arrayDimFetch): Expression
{
$variable = new Variable($variableName);
$assignedArrayDimFetch = clone $arrayDimFetch;
$assign = new Assign($variable, $assignedArrayDimFetch);
return new Expression($assign);
}
/**
* Form might be costructured inside private closure for multiplier
* @see https://doc.nette.org/en/3.0/multiplier

View File

@ -131,6 +131,20 @@ PHP
return new Variable($variableName);
}
private function shouldSkip(ArrayDimFetch $arrayDimFetch): bool
{
if ($this->isBeingAssignedOrInitialized($arrayDimFetch)) {
return true;
}
$parent = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE);
if ($parent instanceof Isset_ || $parent instanceof Unset_) {
return ! $arrayDimFetch->dim instanceof Variable;
}
return false;
}
private function resolveControlType(ArrayDimFetch $arrayDimFetch, string $controlName): ObjectType
{
$controlTypes = $this->methodNamesByInputNamesResolver->resolveExpr($arrayDimFetch);
@ -146,18 +160,4 @@ PHP
return new ObjectType($controlType);
}
private function shouldSkip(ArrayDimFetch $arrayDimFetch): bool
{
if ($this->isBeingAssignedOrInitialized($arrayDimFetch)) {
return true;
}
$parent = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE);
if ($parent instanceof Isset_ || $parent instanceof Unset_) {
return ! $arrayDimFetch->dim instanceof Variable;
}
return false;
}
}

View File

@ -139,6 +139,50 @@ PHP
return $node;
}
private function isGetComponentMethodCallOrArrayDimFetchOnControl(Expr $expr): bool
{
if ($this->isOnClassMethodCall($expr, 'Nette\Application\UI\Control', 'getComponent')) {
return true;
}
return $this->isArrayDimFetchStringOnControlVariable($expr);
}
private function resolveControlType(Assign $assign): Type
{
if ($assign->expr instanceof MethodCall) {
/** @var MethodCall $methodCall */
$methodCall = $assign->expr;
return $this->resolveCreateComponentMethodCallReturnType($methodCall);
}
if ($assign->expr instanceof ArrayDimFetch) {
/** @var ArrayDimFetch $arrayDimFetch */
$arrayDimFetch = $assign->expr;
return $this->resolveArrayDimFetchControlType($arrayDimFetch);
}
return new MixedType();
}
private function isArrayDimFetchStringOnControlVariable(Expr $expr): bool
{
if (! $expr instanceof ArrayDimFetch) {
return false;
}
if (! $expr->dim instanceof String_) {
return false;
}
$varStaticType = $this->getStaticType($expr->var);
if (! $varStaticType instanceof TypeWithClassName) {
return false;
}
return is_a($varStaticType->getClassName(), 'Nette\Application\UI\Control', true);
}
private function resolveCreateComponentMethodCallReturnType(MethodCall $methodCall): Type
{
/** @var Scope|null $scope */
@ -159,15 +203,6 @@ PHP
return $this->resolveTypeFromShortControlNameAndVariable($firstArgumentValue, $scope, $methodCall->var);
}
private function isGetComponentMethodCallOrArrayDimFetchOnControl(Expr $expr): bool
{
if ($this->isOnClassMethodCall($expr, 'Nette\Application\UI\Control', 'getComponent')) {
return true;
}
return $this->isArrayDimFetchStringOnControlVariable($expr);
}
private function resolveArrayDimFetchControlType(ArrayDimFetch $arrayDimFetch): Type
{
/** @var Scope|null $scope */
@ -179,24 +214,6 @@ PHP
return $this->resolveTypeFromShortControlNameAndVariable($arrayDimFetch->dim, $scope, $arrayDimFetch->var);
}
private function isArrayDimFetchStringOnControlVariable(Expr $expr): bool
{
if (! $expr instanceof ArrayDimFetch) {
return false;
}
if (! $expr->dim instanceof String_) {
return false;
}
$varStaticType = $this->getStaticType($expr->var);
if (! $varStaticType instanceof TypeWithClassName) {
return false;
}
return is_a($varStaticType->getClassName(), 'Nette\Application\UI\Control', true);
}
private function resolveTypeFromShortControlNameAndVariable(
String_ $shortControlString,
Scope $scope,
@ -219,21 +236,4 @@ PHP
return ParametersAcceptorSelector::selectSingle($method->getVariants())->getReturnType();
}
private function resolveControlType(Assign $assign): Type
{
if ($assign->expr instanceof MethodCall) {
/** @var MethodCall $methodCall */
$methodCall = $assign->expr;
return $this->resolveCreateComponentMethodCallReturnType($methodCall);
}
if ($assign->expr instanceof ArrayDimFetch) {
/** @var ArrayDimFetch $arrayDimFetch */
$arrayDimFetch = $assign->expr;
return $this->resolveArrayDimFetchControlType($arrayDimFetch);
}
return new MixedType();
}
}

View File

@ -125,21 +125,6 @@ PHP
});
}
private function isInjectProperty(Property $property): bool
{
if (! $property->isPublic()) {
return false;
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
return (bool) $phpDocInfo->getTagsByName('inject');
}
private function removeInjectAnnotation(Property $property): void
{
/** @var PhpDocInfo $phpDocInfo */
@ -155,4 +140,19 @@ PHP
$this->makePrivate($injectProperty);
}
}
private function isInjectProperty(Property $property): bool
{
if (! $property->isPublic()) {
return false;
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
return (bool) $phpDocInfo->getTagsByName('inject');
}
}

View File

@ -106,17 +106,6 @@ final class EventAndListenerTreeProvider
return $this->eventAndListenerTrees;
}
private function resolveMagicProperty(MethodCall $methodCall): ?Property
{
/** @var string $methodName */
$methodName = $this->nodeNameResolver->getName($methodCall->name);
/** @var Class_ $classLike */
$classLike = $methodCall->getAttribute(AttributeKey::CLASS_NODE);
return $classLike->getProperty($methodName);
}
private function initializeEventAndListenerTrees(): void
{
if ($this->eventAndListenerTrees !== [] && ! StaticPHPUnitEnvironment::isPHPUnitRun()) {
@ -161,6 +150,17 @@ final class EventAndListenerTreeProvider
}
}
private function resolveMagicProperty(MethodCall $methodCall): ?Property
{
/** @var string $methodName */
$methodName = $this->nodeNameResolver->getName($methodCall->name);
/** @var Class_ $classLike */
$classLike = $methodCall->getAttribute(AttributeKey::CLASS_NODE);
return $classLike->getProperty($methodName);
}
/**
* @return ClassMethod[][]
*/

View File

@ -84,21 +84,6 @@ final class EventClassNaming
return $this->prependShortClassEventWithNamespace($shortEventClass, $class);
}
/**
* TomatoMarket, onBuy
*
* TomatoMarketBuyEvent
*/
private function createShortEventClassNameFromClassAndProperty(string $class, string $property): string
{
$shortClassName = $this->classNaming->getShortName($class);
// "onMagic" => "Magic"
$shortPropertyName = Strings::substring($property, strlen('on'));
return $shortClassName . $shortPropertyName . self::EVENT;
}
private function getShortEventClassName(MethodCall $methodCall): string
{
/** @var string $methodName */
@ -116,4 +101,19 @@ final class EventClassNaming
return $namespaceAbove . '\\Event\\' . $shortEventClassName;
}
/**
* TomatoMarket, onBuy
*
* TomatoMarketBuyEvent
*/
private function createShortEventClassNameFromClassAndProperty(string $class, string $property): string
{
$shortClassName = $this->classNaming->getShortName($class);
// "onMagic" => "Magic"
$shortPropertyName = Strings::substring($property, strlen('on'));
return $shortClassName . $shortPropertyName . self::EVENT;
}
}

View File

@ -79,54 +79,6 @@ final class VariableNaming
return StaticRectorStrings::underscoreToPascalCase($variableName);
}
private function resolveFromPropertyFetch(PropertyFetch $propertyFetch): string
{
$varName = $this->nodeNameResolver->getName($propertyFetch->var);
if (! is_string($varName)) {
throw new NotImplementedException();
}
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
if (! is_string($propertyName)) {
throw new NotImplementedException();
}
return $varName . ucfirst($propertyName);
}
private function resolveFromMethodCall(MethodCall $methodCall): string
{
$varName = $this->nodeNameResolver->getName($methodCall->var);
if (! is_string($varName)) {
throw new NotImplementedException();
}
$methodName = $this->nodeNameResolver->getName($methodCall->name);
if (! is_string($methodName)) {
throw new NotImplementedException();
}
return $varName . ucfirst($methodName);
}
private function resolveParamNameFromArrayDimFetch(ArrayDimFetch $arrayDimFetch): string
{
while ($arrayDimFetch instanceof ArrayDimFetch) {
if ($arrayDimFetch->dim instanceof Scalar) {
$valueName = $this->nodeNameResolver->getName($arrayDimFetch->var);
$dimName = $this->valueResolver->getValue($arrayDimFetch->dim);
$dimName = StaticRectorStrings::underscoreToCamelCase($dimName);
return $valueName . $dimName;
}
$arrayDimFetch = $arrayDimFetch->var;
}
return $this->resolveBareFromNode($arrayDimFetch);
}
private function resolveBareFromNode(Node $node): string
{
$node = $this->unwrapNode($node);
@ -163,16 +115,6 @@ final class VariableNaming
throw new NotImplementedException();
}
private function resolveFromNew(New_ $new): string
{
if ($new->class instanceof Name) {
$className = $this->nodeNameResolver->getName($new->class);
return $this->classNaming->getShortName($className);
}
throw new NotImplementedYetException();
}
private function unwrapNode(Node $node): ?Node
{
if ($node instanceof Arg) {
@ -189,4 +131,62 @@ final class VariableNaming
return $node;
}
private function resolveParamNameFromArrayDimFetch(ArrayDimFetch $arrayDimFetch): string
{
while ($arrayDimFetch instanceof ArrayDimFetch) {
if ($arrayDimFetch->dim instanceof Scalar) {
$valueName = $this->nodeNameResolver->getName($arrayDimFetch->var);
$dimName = $this->valueResolver->getValue($arrayDimFetch->dim);
$dimName = StaticRectorStrings::underscoreToCamelCase($dimName);
return $valueName . $dimName;
}
$arrayDimFetch = $arrayDimFetch->var;
}
return $this->resolveBareFromNode($arrayDimFetch);
}
private function resolveFromPropertyFetch(PropertyFetch $propertyFetch): string
{
$varName = $this->nodeNameResolver->getName($propertyFetch->var);
if (! is_string($varName)) {
throw new NotImplementedException();
}
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
if (! is_string($propertyName)) {
throw new NotImplementedException();
}
return $varName . ucfirst($propertyName);
}
private function resolveFromMethodCall(MethodCall $methodCall): string
{
$varName = $this->nodeNameResolver->getName($methodCall->var);
if (! is_string($varName)) {
throw new NotImplementedException();
}
$methodName = $this->nodeNameResolver->getName($methodCall->name);
if (! is_string($methodName)) {
throw new NotImplementedException();
}
return $varName . ucfirst($methodName);
}
private function resolveFromNew(New_ $new): string
{
if ($new->class instanceof Name) {
$className = $this->nodeNameResolver->getName($new->class);
return $this->classNaming->getShortName($className);
}
throw new NotImplementedYetException();
}
}

View File

@ -65,30 +65,6 @@ final class EventValueObjectClassFactory
return $this->wrapClassToNamespace($className, $class);
}
/**
* @param VariableWithType[] $variableWithTypes
*/
private function createConstructClassMethod(array $variableWithTypes): ClassMethod
{
$methodBuilder = new MethodBuilder(MethodName::CONSTRUCT);
$methodBuilder->makePublic();
foreach ($variableWithTypes as $variableWithType) {
$param = new Param(new Variable($variableWithType->getName()));
if ($variableWithType->getPhpParserTypeNode() !== null) {
$param->type = $variableWithType->getPhpParserTypeNode();
}
$methodBuilder->addParam($param);
$assign = $this->nodeFactory->createPropertyAssignment($variableWithType->getName());
$methodBuilder->addStmt($assign);
}
return $methodBuilder->getNode();
}
private function createEventClassBuilder(string $className): ClassBuilder
{
$shortClassName = $this->classNaming->getShortName($className);
@ -100,16 +76,6 @@ final class EventValueObjectClassFactory
return $classBuilder;
}
private function wrapClassToNamespace(string $className, Class_ $class): Namespace_
{
$namespace = Strings::before($className, '\\', -1);
$namespaceBuilder = new NamespaceBuilder($namespace);
$namespaceBuilder->addStmt($class);
return $namespaceBuilder->getNode();
}
/**
* @param Arg[] $args
*/
@ -147,6 +113,16 @@ final class EventValueObjectClassFactory
}
}
private function wrapClassToNamespace(string $className, Class_ $class): Namespace_
{
$namespace = Strings::before($className, '\\', -1);
$namespaceBuilder = new NamespaceBuilder($namespace);
$namespaceBuilder->addStmt($class);
return $namespaceBuilder->getNode();
}
/**
* @param VariableWithType[] $variablesWithTypes
*/
@ -170,4 +146,28 @@ final class EventValueObjectClassFactory
$usedVariableNames[] = $variablesWithType->getName();
}
}
/**
* @param VariableWithType[] $variableWithTypes
*/
private function createConstructClassMethod(array $variableWithTypes): ClassMethod
{
$methodBuilder = new MethodBuilder(MethodName::CONSTRUCT);
$methodBuilder->makePublic();
foreach ($variableWithTypes as $variableWithType) {
$param = new Param(new Variable($variableWithType->getName()));
if ($variableWithType->getPhpParserTypeNode() !== null) {
$param->type = $variableWithType->getPhpParserTypeNode();
}
$methodBuilder->addParam($param);
$assign = $this->nodeFactory->createPropertyAssignment($variableWithType->getName());
$methodBuilder->addStmt($assign);
}
return $methodBuilder->getNode();
}
}

View File

@ -109,6 +109,15 @@ final class ListeningClassMethodArgumentManipulator
}
}
private function changeClassParamToEventClass(string $eventClass, ClassMethod $classMethod): void
{
$paramName = $this->classNaming->getVariableName($eventClass);
$eventVariable = new Variable($paramName);
$param = new Param($eventVariable, null, new FullyQualified($eventClass));
$classMethod->params = [$param];
}
private function isParamUsedInClassMethodBody(ClassMethod $classMethod, Param $param): bool
{
return (bool) $this->betterNodeFinder->findFirst((array) $classMethod->stmts, function (Node $node) use (
@ -122,15 +131,6 @@ final class ListeningClassMethodArgumentManipulator
});
}
private function changeClassParamToEventClass(string $eventClass, ClassMethod $classMethod): void
{
$paramName = $this->classNaming->getVariableName($eventClass);
$eventVariable = new Variable($paramName);
$param = new Param($eventVariable, null, new FullyQualified($eventClass));
$classMethod->params = [$param];
}
private function createEventGetterToVariableMethodCall(
string $eventClass,
Param $param,

View File

@ -123,26 +123,6 @@ final class ListeningMethodsCollector
return $this->classMethodsByEventClass;
}
private function resolveCustomClassMethodAndEventClass(
ArrayItem $arrayItem,
Class_ $class,
string $eventClass
): array {
// custom method name
$classMethodName = $this->valueResolver->getValue($arrayItem->value);
$classMethod = $class->getMethod($classMethodName);
if (Strings::contains($eventClass, '::')) {
[$dispatchingClass, $property] = Strings::split($eventClass, '#::#');
$eventClass = $this->eventClassNaming->createEventClassNameFromClassAndProperty(
$dispatchingClass,
$property
);
}
return [$classMethod, $eventClass];
}
private function matchClassMethodByNodeValue(Class_ $class, Expr $expr): ?ClassMethod
{
$possibleMethodName = $this->valueResolver->getValue($expr);
@ -165,4 +145,24 @@ final class ListeningMethodsCollector
$this->classMethodsByEventClass[$eventClass] = $classMethod;
}
private function resolveCustomClassMethodAndEventClass(
ArrayItem $arrayItem,
Class_ $class,
string $eventClass
): array {
// custom method name
$classMethodName = $this->valueResolver->getValue($arrayItem->value);
$classMethod = $class->getMethod($classMethodName);
if (Strings::contains($eventClass, '::')) {
[$dispatchingClass, $property] = Strings::split($eventClass, '#::#');
$eventClass = $this->eventClassNaming->createEventClassNameFromClassAndProperty(
$dispatchingClass,
$property
);
}
return [$classMethod, $eventClass];
}
}

View File

@ -134,16 +134,6 @@ PHP
return $node;
}
private function resolveMethodNameFromKdybyEventName(Expr $expr): string
{
$kdybyEventName = $this->getValue($expr);
if (Strings::contains($kdybyEventName, '::')) {
return (string) Strings::after($kdybyEventName, '::', - 1);
}
throw new NotImplementedException($kdybyEventName);
}
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
{
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
@ -158,19 +148,6 @@ PHP
return ! $this->isName($classMethod, 'getSubscribedEvents');
}
private function refactorArrayWithEventTable(Array_ $array): void
{
foreach ($array->items as $arrayItem) {
if ($arrayItem->key !== null) {
continue;
}
$methodName = $this->resolveMethodNameFromKdybyEventName($arrayItem->value);
$arrayItem->key = $arrayItem->value;
$arrayItem->value = new String_($methodName);
}
}
private function refactorEventNames(ClassMethod $classMethod): void
{
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) {
@ -192,4 +169,27 @@ PHP
$this->getSubscribedEventsArrayManipulator->change($returnedExpr);
});
}
private function refactorArrayWithEventTable(Array_ $array): void
{
foreach ($array->items as $arrayItem) {
if ($arrayItem->key !== null) {
continue;
}
$methodName = $this->resolveMethodNameFromKdybyEventName($arrayItem->value);
$arrayItem->key = $arrayItem->value;
$arrayItem->value = new String_($methodName);
}
}
private function resolveMethodNameFromKdybyEventName(Expr $expr): string
{
$kdybyEventName = $this->getValue($expr);
if (Strings::contains($kdybyEventName, '::')) {
return (string) Strings::after($kdybyEventName, '::', - 1);
}
throw new NotImplementedException($kdybyEventName);
}
}

View File

@ -100,28 +100,6 @@ PHP
return $this->createAssign($node);
}
private function resolveControlName(MethodCall $methodCall): string
{
$controlName = $methodCall->args[0]->value;
if (! $controlName instanceof String_) {
throw new ShouldNotHappenException();
}
return $controlName->value . 'DateControl';
}
private function createDateTimeControlNew(MethodCall $methodCall): New_
{
$fullyQualified = new FullyQualified('Nextras\FormComponents\Controls\DateControl');
$new = new New_($fullyQualified);
if (isset($methodCall->args[1])) {
$new->args[] = $methodCall->args[1];
}
return $new;
}
private function createAssign(MethodCall $methodCall): ?Node
{
$key = $methodCall->args[0]->value;
@ -152,4 +130,26 @@ PHP
return $formAssign;
}
private function resolveControlName(MethodCall $methodCall): string
{
$controlName = $methodCall->args[0]->value;
if (! $controlName instanceof String_) {
throw new ShouldNotHappenException();
}
return $controlName->value . 'DateControl';
}
private function createDateTimeControlNew(MethodCall $methodCall): New_
{
$fullyQualified = new FullyQualified('Nextras\FormComponents\Controls\DateControl');
$new = new New_($fullyQualified);
if (isset($methodCall->args[1])) {
$new->args[] = $methodCall->args[1];
}
return $new;
}
}

View File

@ -95,35 +95,6 @@ PHP
$this->skipPatterns = $configuration[self::SKIP_PATTERNS] ?? [];
}
/**
* @return Param[]
*/
private function getSortedParams(ClassMethod $classMethod): array
{
$params = $classMethod->getParams();
usort($params, function (Param $firstParam, Param $secondParam) {
/** @var Name $firstParamType */
$firstParamType = $this->getParamType($firstParam);
/** @var Name $secondParamType */
$secondParamType = $this->getParamType($secondParam);
return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType);
});
return $params;
}
private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool
{
foreach ($classMethod->params as $param) {
if ($param->type instanceof Identifier) {
return true;
}
}
return false;
}
private function shouldSkip(ClassMethod $classMethod): bool
{
if (! $this->isName($classMethod, MethodName::CONSTRUCT)) {
@ -148,22 +119,21 @@ PHP
}
/**
* @return Identifier|Name|UnionType|null
* @return Param[]
*/
private function getParamType(Param $param)
private function getSortedParams(ClassMethod $classMethod): array
{
return $param->type instanceof NullableType ? $param->type->type : $param->type;
}
$params = $classMethod->getParams();
usort($params, function (Param $firstParam, Param $secondParam) {
/** @var Name $firstParamType */
$firstParamType = $this->getParamType($firstParam);
/** @var Name $secondParamType */
$secondParamType = $this->getParamType($secondParam);
private function hasParamWithNoType(ClassMethod $classMethod): bool
{
foreach ($classMethod->params as $param) {
if ($param->type === null) {
return true;
}
}
return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType);
});
return false;
return $params;
}
/**
@ -183,4 +153,34 @@ PHP
return false;
}
private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool
{
foreach ($classMethod->params as $param) {
if ($param->type instanceof Identifier) {
return true;
}
}
return false;
}
private function hasParamWithNoType(ClassMethod $classMethod): bool
{
foreach ($classMethod->params as $param) {
if ($param->type === null) {
return true;
}
}
return false;
}
/**
* @return Identifier|Name|UnionType|null
*/
private function getParamType(Param $param)
{
return $param->type instanceof NullableType ? $param->type->type : $param->type;
}
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
@ -18,6 +19,11 @@ use Rector\Order\StmtOrder;
*/
final class OrderPrivateMethodsByUseRector extends AbstractRector
{
/**
* @var int
*/
private const MAX_ATTEMTPS = 5;
/**
* @var StmtOrder
*/
@ -86,32 +92,58 @@ PHP
*/
public function refactor(Node $node): ?Node
{
$desiredClassMethodOrder = $this->getLocalMethodCallOrder($node);
$privateClassMethodsByKey = $this->resolvePrivateClassMethods($node);
[$desiredPrivateMethodCallOrder, $privateClassMethodsByKey] = $this->getPrivateMethodCallOrderAndClassMethods(
$node
);
// order is correct, nothing to change
if ($privateClassMethodsByKey === $desiredClassMethodOrder) {
if ($privateClassMethodsByKey === $desiredPrivateMethodCallOrder) {
return null;
}
// different private method count, one of them is dead probably
if (count($desiredClassMethodOrder) !== count($desiredClassMethodOrder)) {
if (count($desiredPrivateMethodCallOrder) !== count($privateClassMethodsByKey)) {
return null;
}
$oldToNewKeys = $this->stmtOrder->createOldToNewKeys($desiredClassMethodOrder, $privateClassMethodsByKey);
$attempt = 0;
while (array_values($desiredPrivateMethodCallOrder) !== array_values($privateClassMethodsByKey)) {
$attempt++;
if ($attempt >= self::MAX_ATTEMTPS) {
throw new ShouldNotHappenException('Number of attempts to reorder the methods exceeded');
}
return $this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys);
$oldToNewKeys = $this->stmtOrder->createOldToNewKeys(
$desiredPrivateMethodCallOrder,
$privateClassMethodsByKey
);
$node = $this->stmtOrder->reorderClassStmtsByOldToNewKeys($node, $oldToNewKeys);
[$desiredPrivateMethodCallOrder, $privateClassMethodsByKey] = $this->getPrivateMethodCallOrderAndClassMethods(
$node
);
}
return $node;
}
private function getPrivateMethodCallOrderAndClassMethods(Class_ $class): array
{
return [$this->getLocalPrivateMethodCallOrder($class), $this->resolvePrivateClassMethods($class)];
}
/**
* @return string[]
*/
private function getLocalMethodCallOrder(Class_ $class): array
private function getLocalPrivateMethodCallOrder(Class_ $class): array
{
$localMethodCallInOrder = [];
$localPrivateMethodCallInOrder = [];
$this->traverseNodesWithCallable($class->getMethods(), function (Node $node) use (&$localMethodCallInOrder) {
$this->traverseNodesWithCallable($class->getMethods(), function (Node $node) use (
&$localPrivateMethodCallInOrder,
$class
) {
if (! $node instanceof MethodCall) {
return null;
}
@ -125,12 +157,19 @@ PHP
return null;
}
$localMethodCallInOrder[] = $methodName;
$classMethod = $class->getMethod($methodName);
if ($classMethod === null) {
return null;
}
if ($classMethod->isPrivate()) {
$localPrivateMethodCallInOrder[] = $methodName;
}
return null;
});
return array_unique($localMethodCallInOrder);
return array_unique($localPrivateMethodCallInOrder);
}
private function resolvePrivateClassMethods(Class_ $class): array

View File

@ -132,19 +132,6 @@ PHP
return $node;
}
private function getVisibilityAsString(Property $property): string
{
if ($property->isPrivate()) {
return 'private';
}
if ($property->isProtected()) {
return 'protected';
}
return 'public';
}
/**
* @return Property[][]
*/
@ -177,4 +164,17 @@ PHP
return array_keys($propertyNameToRank);
}
private function getVisibilityAsString(Property $property): string
{
if ($property->isPrivate()) {
return 'private';
}
if ($property->isProtected()) {
return 'protected';
}
return 'public';
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
class DifferentAmountOfMethodsClass
{
public function run()
{
$this->call1();
$this->call2();
$this->publicCall1();
}
public function publicCall1()
{
}
private function call2()
{
}
private function call1()
{
}
private function call3()
{
}
}
?>

View File

@ -0,0 +1,165 @@
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\SmartFileSystem\SmartFileInfo;
class MultipleRunsClass
{
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
$node->params = $this->getSortedParams($node);
return $node;
}
public function configure(array $configuration): void
{
}
private function getSortedParams(ClassMethod $classMethod): array
{
$params = $classMethod->getParams();
usort($params, function (Param $firstParam, Param $secondParam) {
$firstParamType = $this->getParamType($firstParam);
$secondParamType = $this->getParamType($secondParam);
return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType);
});
return $params;
}
private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool
{
}
private function shouldSkip(ClassMethod $classMethod): bool
{
$smartFileInfo = $classMethod->getAttribute(AttributeKey::FILE_INFO);
if ($this->isFileInfoMatch($smartFileInfo)) {
return true;
}
if ($this->hasPrimitiveDataTypeParam($classMethod)) {
return true;
}
return $this->hasParamWithNoType($classMethod);
}
private function getParamType(Param $param)
{
}
private function hasParamWithNoType(ClassMethod $classMethod): bool
{
}
private function isFileInfoMatch(SmartFileInfo $smartFileInfo): bool
{
foreach ($this->skipPatterns as $pattern) {
if (fnmatch($pattern, $smartFileInfo->getRelativeFilePath(), FNM_NOESCAPE)) {
return true;
}
}
return false;
}
}
?>
-----
<?php
namespace Rector\Order\Tests\Rector\Class_\OrderPrivateMethodsByUseRector\Fixture;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\SmartFileSystem\SmartFileInfo;
class MultipleRunsClass
{
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
$node->params = $this->getSortedParams($node);
return $node;
}
public function configure(array $configuration): void
{
}
private function shouldSkip(ClassMethod $classMethod): bool
{
$smartFileInfo = $classMethod->getAttribute(AttributeKey::FILE_INFO);
if ($this->isFileInfoMatch($smartFileInfo)) {
return true;
}
if ($this->hasPrimitiveDataTypeParam($classMethod)) {
return true;
}
return $this->hasParamWithNoType($classMethod);
}
private function getSortedParams(ClassMethod $classMethod): array
{
$params = $classMethod->getParams();
usort($params, function (Param $firstParam, Param $secondParam) {
$firstParamType = $this->getParamType($firstParam);
$secondParamType = $this->getParamType($secondParam);
return $this->getShortName($firstParamType) <=> $this->getShortName($secondParamType);
});
return $params;
}
private function isFileInfoMatch(SmartFileInfo $smartFileInfo): bool
{
foreach ($this->skipPatterns as $pattern) {
if (fnmatch($pattern, $smartFileInfo->getRelativeFilePath(), FNM_NOESCAPE)) {
return true;
}
}
return false;
}
private function hasPrimitiveDataTypeParam(ClassMethod $classMethod): bool
{
}
private function hasParamWithNoType(ClassMethod $classMethod): bool
{
}
private function getParamType(Param $param)
{
}
}
?>

View File

@ -153,26 +153,19 @@ PHP
$this->secondValue = null;
}
private function processTernary(Ternary $ternary): void
private function matchOnEqualFirstValueAndSecondValue(If_ $if): void
{
if ($ternary->cond instanceof Smaller) {
$this->firstValue = $ternary->cond->left;
$this->secondValue = $ternary->cond->right;
$this->matchOnEqual($if);
if ($ternary->if !== null) {
$this->onSmaller = $this->getValue($ternary->if);
if ($if->else !== null) {
$this->processElse($if->else);
} else {
$this->nextNode = $if->getAttribute(AttributeKey::NEXT_NODE);
if ($this->nextNode instanceof Return_ && $this->nextNode->expr instanceof Ternary) {
/** @var Ternary $ternary */
$ternary = $this->nextNode->expr;
$this->processTernary($ternary);
}
$this->onGreater = $this->getValue($ternary->else);
} elseif ($ternary->cond instanceof Greater) {
$this->firstValue = $ternary->cond->right;
$this->secondValue = $ternary->cond->left;
if ($ternary->if !== null) {
$this->onGreater = $this->getValue($ternary->if);
}
$this->onSmaller = $this->getValue($ternary->else);
}
}
@ -194,22 +187,6 @@ PHP
);
}
private function matchOnEqualFirstValueAndSecondValue(If_ $if): void
{
$this->matchOnEqual($if);
if ($if->else !== null) {
$this->processElse($if->else);
} else {
$this->nextNode = $if->getAttribute(AttributeKey::NEXT_NODE);
if ($this->nextNode instanceof Return_ && $this->nextNode->expr instanceof Ternary) {
/** @var Ternary $ternary */
$ternary = $this->nextNode->expr;
$this->processTernary($ternary);
}
}
}
private function matchOnEqual(If_ $if): void
{
if (count($if->stmts) !== 1) {
@ -243,4 +220,27 @@ PHP
$this->processTernary($returnNode->expr);
}
}
private function processTernary(Ternary $ternary): void
{
if ($ternary->cond instanceof Smaller) {
$this->firstValue = $ternary->cond->left;
$this->secondValue = $ternary->cond->right;
if ($ternary->if !== null) {
$this->onSmaller = $this->getValue($ternary->if);
}
$this->onGreater = $this->getValue($ternary->else);
} elseif ($ternary->cond instanceof Greater) {
$this->firstValue = $ternary->cond->right;
$this->secondValue = $ternary->cond->left;
if ($ternary->if !== null) {
$this->onGreater = $this->getValue($ternary->if);
}
$this->onSmaller = $this->getValue($ternary->else);
}
}
}

View File

@ -166,6 +166,28 @@ PHP
$this->classLikeTypeOnly = $configuration[self::CLASS_LIKE_TYPE_ONLY] ?? false;
}
/**
* @param Name|NullableType|PhpParserUnionType $node
*/
private function shouldSkipNonClassLikeType(Node $node): bool
{
if (! $this->classLikeTypeOnly) {
return false;
}
// unwrap nullable type
if ($node instanceof NullableType) {
$node = $node->type;
}
$typeName = $this->getName($node);
if ($typeName === null) {
return false;
}
return ! ClassExistenceStaticHelper::doesClassLikeExist($typeName);
}
private function removeVarPhpTagValueNodeIfNotComment(Property $property, Type $type): void
{
// keep doctrine collection narrow type
@ -244,26 +266,4 @@ PHP
{
return $varTagValueNode->type instanceof ArrayTypeNode;
}
/**
* @param Name|NullableType|PhpParserUnionType $node
*/
private function shouldSkipNonClassLikeType(Node $node): bool
{
if (! $this->classLikeTypeOnly) {
return false;
}
// unwrap nullable type
if ($node instanceof NullableType) {
$node = $node->type;
}
$typeName = $this->getName($node);
if ($typeName === null) {
return false;
}
return ! ClassExistenceStaticHelper::doesClassLikeExist($typeName);
}
}

View File

@ -285,6 +285,22 @@ final class TokenManipulator
return $this->valueResolver->isValue($node->dim, $value);
}
/**
* @return ArrayDimFetch[]|ConstFetch[]|null
*/
private function matchTokenArrayDimFetchAndTConstantType(Identical $identical): ?array
{
if ($identical->left instanceof ArrayDimFetch && $identical->right instanceof ConstFetch) {
return [$identical->left, $identical->right];
}
if ($identical->right instanceof ArrayDimFetch && $identical->left instanceof ConstFetch) {
return [$identical->right, $identical->left];
}
return null;
}
private function createIsTConstTypeMethodCall(ArrayDimFetch $arrayDimFetch, ConstFetch $constFetch): MethodCall
{
return new MethodCall($arrayDimFetch->var, 'is', [new Arg($constFetch)]);
@ -310,22 +326,6 @@ final class TokenManipulator
return false;
}
/**
* @return ArrayDimFetch[]|ConstFetch[]|null
*/
private function matchTokenArrayDimFetchAndTConstantType(Identical $identical): ?array
{
if ($identical->left instanceof ArrayDimFetch && $identical->right instanceof ConstFetch) {
return [$identical->left, $identical->right];
}
if ($identical->right instanceof ArrayDimFetch && $identical->left instanceof ConstFetch) {
return [$identical->right, $identical->left];
}
return null;
}
private function matchParentNodeInCaseOfIdenticalTrue(FuncCall $funcCall): Node
{
$parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE);

View File

@ -103,19 +103,27 @@ PHP
return $node;
}
private function matchClassMethodParamByAssignedVariable(
ClassMethod $classMethod,
Expr $assignedExpr
): ?Param {
foreach ($classMethod->params as $param) {
if (! $this->areNodesEqual($assignedExpr, $param->var)) {
/**
* @return PromotionCandidate[]
*/
private function collectPromotionCandidatesFromClass(Class_ $class): array
{
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if ($constructClassMethod === null) {
return [];
}
$this->promotionCandidates = [];
foreach ($class->getProperties() as $property) {
if (count($property->props) !== 1) {
continue;
}
return $param;
$this->collectPromotionCandidate($property, $constructClassMethod);
}
return null;
return $this->promotionCandidates;
}
private function collectPromotionCandidate(Property $property, ClassMethod $constructClassMethod): void
@ -152,26 +160,18 @@ PHP
}
}
/**
* @return PromotionCandidate[]
*/
private function collectPromotionCandidatesFromClass(Class_ $class): array
{
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if ($constructClassMethod === null) {
return [];
}
$this->promotionCandidates = [];
foreach ($class->getProperties() as $property) {
if (count($property->props) !== 1) {
private function matchClassMethodParamByAssignedVariable(
ClassMethod $classMethod,
Expr $assignedExpr
): ?Param {
foreach ($classMethod->params as $param) {
if (! $this->areNodesEqual($assignedExpr, $param->var)) {
continue;
}
$this->collectPromotionCandidate($property, $constructClassMethod);
return $param;
}
return $this->promotionCandidates;
return null;
}
}

View File

@ -99,6 +99,24 @@ PHP
return $this->createStaticCall('PhpToken', 'getAll', $node->args);
}
private function refactorTokensVariable(FuncCall $funcCall): void
{
$assign = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
if (! $assign instanceof Assign) {
return;
}
$classMethodOrFunctionNode = $funcCall->getAttribute(AttributeKey::METHOD_NODE) ?:
$funcCall->getAttribute(AttributeKey::FUNCTION_NODE);
if ($classMethodOrFunctionNode === null) {
return;
}
// dummy approach, improve when needed
$this->replaceGetNameOrGetValue($classMethodOrFunctionNode, $assign->var);
}
/**
* @param ClassMethod|Function_ $functionLike
*/
@ -110,6 +128,23 @@ PHP
}
}
/**
* @param ClassMethod|Function_ $functionLike
* @return Foreach_[]
*/
private function findForeachesOverTokenVariable(FunctionLike $functionLike, Expr $assignedExpr): array
{
return $this->betterNodeFinder->find((array) $functionLike->stmts, function (Node $node) use (
$assignedExpr
): bool {
if (! $node instanceof Foreach_) {
return false;
}
return $this->areNodesEqual($node->expr, $assignedExpr);
});
}
private function refactorTokenInForeach(Foreach_ $tokensForeach): void
{
$singleToken = $tokensForeach->valueVar;
@ -135,39 +170,4 @@ PHP
}
});
}
private function refactorTokensVariable(FuncCall $funcCall): void
{
$assign = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
if (! $assign instanceof Assign) {
return;
}
$classMethodOrFunctionNode = $funcCall->getAttribute(AttributeKey::METHOD_NODE) ?:
$funcCall->getAttribute(AttributeKey::FUNCTION_NODE);
if ($classMethodOrFunctionNode === null) {
return;
}
// dummy approach, improve when needed
$this->replaceGetNameOrGetValue($classMethodOrFunctionNode, $assign->var);
}
/**
* @param ClassMethod|Function_ $functionLike
* @return Foreach_[]
*/
private function findForeachesOverTokenVariable(FunctionLike $functionLike, Expr $assignedExpr): array
{
return $this->betterNodeFinder->find((array) $functionLike->stmts, function (Node $node) use (
$assignedExpr
): bool {
if (! $node instanceof Foreach_) {
return false;
}
return $this->areNodesEqual($node->expr, $assignedExpr);
});
}
}

View File

@ -129,33 +129,6 @@ PHP
return false;
}
/**
* @param Case_[] $cases
* @return MatchArm[]
*/
private function createMatchArmsFromCases(array $cases): array
{
$matchArms = [];
foreach ($cases as $case) {
$stmt = $case->stmts[0];
if (! $stmt instanceof Expression) {
throw new ShouldNotHappenException();
}
$expr = $stmt->expr;
if ($expr instanceof Assign) {
$this->assignExpr = $expr->var;
$expr = $expr->expr;
}
$condList = $case->cond === null ? null : [$case->cond];
$matchArms[] = new MatchArm($condList, $expr);
}
return $matchArms;
}
private function hasSingleStmtCases(Switch_ $switch): bool
{
foreach ($switch->cases as $case) {
@ -191,4 +164,31 @@ PHP
return count($assignVariableNames) <= 1;
}
/**
* @param Case_[] $cases
* @return MatchArm[]
*/
private function createMatchArmsFromCases(array $cases): array
{
$matchArms = [];
foreach ($cases as $case) {
$stmt = $case->stmts[0];
if (! $stmt instanceof Expression) {
throw new ShouldNotHappenException();
}
$expr = $stmt->expr;
if ($expr instanceof Assign) {
$this->assignExpr = $expr->var;
$expr = $expr->expr;
}
$condList = $case->cond === null ? null : [$case->cond];
$matchArms[] = new MatchArm($condList, $expr);
}
return $matchArms;
}
}

View File

@ -258,6 +258,75 @@ PHP
return $dataProviderClassMethods;
}
/**
* @param string[] $singleConfiguration
*/
private function isMethodCallMatch(MethodCall $methodCall, array $singleConfiguration): bool
{
if (! $this->isObjectType($methodCall->var, $singleConfiguration['class'])) {
return false;
}
return $this->isName($methodCall->name, $singleConfiguration['old_method']);
}
private function createDataProviderMethodName(Node $node): string
{
/** @var string $methodName */
$methodName = $node->getAttribute(AttributeKey::METHOD_NAME);
return 'provideDataFor' . ucfirst($methodName);
}
/**
* @return ParamAndArgValueObject[]
*/
private function collectParamAndArgsFromArray(Array_ $array, string $variableName): array
{
$isNestedArray = $this->isNestedArray($array);
if ($isNestedArray) {
return $this->collectParamAndArgsFromNestedArray($array, $variableName);
}
$itemsStaticType = $this->resolveItemStaticType($array, $isNestedArray);
return $this->collectParamAndArgsFromNonNestedArray($array, $variableName, $itemsStaticType);
}
/**
* @param ParamAndArgValueObject[] $paramAndArgs
*/
private function refactorTestClassMethodParams(ClassMethod $classMethod, array $paramAndArgs): void
{
$classMethod->params = $this->createParams($paramAndArgs);
/** @var PhpDocInfo $phpDocInfo */
$phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO);
foreach ($paramAndArgs as $paramAndArg) {
$staticType = $paramAndArg->getType();
if (! $staticType instanceof UnionType) {
continue;
}
/** @var string $paramName */
$paramName = $this->getName($paramAndArg->getVariable());
/** @var TypeNode $staticTypeNode */
$staticTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($staticType);
$paramTagValueNode = $this->createParamTagNode($paramName, $staticTypeNode);
$phpDocInfo->addTagValueNode($paramTagValueNode);
}
}
private function createDataProviderTagNode(string $dataProviderMethodName): PhpDocTagNode
{
return new AttributeAwarePhpDocTagNode('@dataProvider', new GenericTagValueNode(
$dataProviderMethodName . '()'
));
}
private function isNestedArray(Array_ $array): bool
{
foreach ($array->items as $arrayItem) {
@ -354,75 +423,6 @@ PHP
return new AttributeAwareParamTagValueNode($typeNode, false, '$' . $name, '', false);
}
/**
* @param string[] $singleConfiguration
*/
private function isMethodCallMatch(MethodCall $methodCall, array $singleConfiguration): bool
{
if (! $this->isObjectType($methodCall->var, $singleConfiguration['class'])) {
return false;
}
return $this->isName($methodCall->name, $singleConfiguration['old_method']);
}
private function createDataProviderMethodName(Node $node): string
{
/** @var string $methodName */
$methodName = $node->getAttribute(AttributeKey::METHOD_NAME);
return 'provideDataFor' . ucfirst($methodName);
}
/**
* @return ParamAndArgValueObject[]
*/
private function collectParamAndArgsFromArray(Array_ $array, string $variableName): array
{
$isNestedArray = $this->isNestedArray($array);
if ($isNestedArray) {
return $this->collectParamAndArgsFromNestedArray($array, $variableName);
}
$itemsStaticType = $this->resolveItemStaticType($array, $isNestedArray);
return $this->collectParamAndArgsFromNonNestedArray($array, $variableName, $itemsStaticType);
}
/**
* @param ParamAndArgValueObject[] $paramAndArgs
*/
private function refactorTestClassMethodParams(ClassMethod $classMethod, array $paramAndArgs): void
{
$classMethod->params = $this->createParams($paramAndArgs);
/** @var PhpDocInfo $phpDocInfo */
$phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO);
foreach ($paramAndArgs as $paramAndArg) {
$staticType = $paramAndArg->getType();
if (! $staticType instanceof UnionType) {
continue;
}
/** @var string $paramName */
$paramName = $this->getName($paramAndArg->getVariable());
/** @var TypeNode $staticTypeNode */
$staticTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($staticType);
$paramTagValueNode = $this->createParamTagNode($paramName, $staticTypeNode);
$phpDocInfo->addTagValueNode($paramTagValueNode);
}
}
private function createDataProviderTagNode(string $dataProviderMethodName): PhpDocTagNode
{
return new AttributeAwarePhpDocTagNode('@dataProvider', new GenericTagValueNode(
$dataProviderMethodName . '()'
));
}
private function setTypeIfNotNull(ParamAndArgValueObject $paramAndArgValueObject, Param $param): void
{
$staticType = $paramAndArgValueObject->getType();

View File

@ -118,20 +118,6 @@ final class AssertComparisonToSpecificMethodRector extends AbstractPHPUnitRector
return $node;
}
private function isConstantValue(Node $node): bool
{
$nodeClass = get_class($node);
if (in_array($nodeClass, [Array_::class, ConstFetch::class], true)) {
return true;
}
if (is_subclass_of($node, Scalar::class)) {
return true;
}
return $this->isVariableName($node, 'exp*');
}
/**
* @param MethodCall|StaticCall $node
*/
@ -154,4 +140,18 @@ final class AssertComparisonToSpecificMethodRector extends AbstractPHPUnitRector
$node->args = array_merge([$firstArgument, $secondArgument], $oldArguments);
}
private function isConstantValue(Node $node): bool
{
$nodeClass = get_class($node);
if (in_array($nodeClass, [Array_::class, ConstFetch::class], true)) {
return true;
}
if (is_subclass_of($node, Scalar::class)) {
return true;
}
return $this->isVariableName($node, 'exp*');
}
}

View File

@ -152,29 +152,6 @@ PHP
return $phpDocInfo->hasByNames(['api', 'required']);
}
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
{
if ($classMethod->isPrivate()) {
return true;
}
if ($classMethod->isAbstract()) {
return true;
}
// skip for now
if ($classMethod->isStatic()) {
return true;
}
if ($this->isName($classMethod, '__*')) {
return true;
}
// possibly container service factories
return $this->isNames($classMethod, ['create', 'create*']);
}
private function isControllerAction(Class_ $class, ClassMethod $classMethod): bool
{
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
@ -200,4 +177,27 @@ PHP
return $phpDocInfo->hasByName('inject');
}
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
{
if ($classMethod->isPrivate()) {
return true;
}
if ($classMethod->isAbstract()) {
return true;
}
// skip for now
if ($classMethod->isStatic()) {
return true;
}
if ($this->isName($classMethod, '__*')) {
return true;
}
// possibly container service factories
return $this->isNames($classMethod, ['create', 'create*']);
}
}

View File

@ -203,6 +203,16 @@ PHP
}
}
/**
* @param Node[] $nodes
*/
private function hasAtLeast2Classes(array $nodes): bool
{
$classes = $this->betterNodeFinder->findClassLikes($nodes);
return count($classes) > 1;
}
private function createClassLikeFileDestination(ClassLike $classLike, SmartFileInfo $smartFileInfo): string
{
$currentDirectory = dirname($smartFileInfo->getRealPath());
@ -218,14 +228,4 @@ PHP
}
}
}
/**
* @param Node[] $nodes
*/
private function hasAtLeast2Classes(array $nodes): bool
{
$classes = $this->betterNodeFinder->findClassLikes($nodes);
return count($classes) > 1;
}
}

View File

@ -274,6 +274,35 @@ final class ClassRenamer
return $foundClass instanceof ClassLike ? $foundClass : null;
}
/**
* @param string[] $oldToNewClasses
*/
private function renameClassImplements(ClassLike $classLike, array $oldToNewClasses): void
{
if (! $classLike instanceof Class_) {
return;
}
foreach ((array) $classLike->implements as $key => $implementName) {
if (! $implementName instanceof Name) {
continue;
}
if (! $implementName->getAttribute(AttributeKey::VIRTUAL_NODE)) {
continue;
}
$namespaceName = $classLike->getAttribute(AttributeKey::NAMESPACE_NAME);
$fullyQualifiedName = $namespaceName . '\\' . $implementName->toString();
$newName = $oldToNewClasses[$fullyQualifiedName] ?? null;
if ($newName === null) {
continue;
}
$classLike->implements[$key] = new FullyQualified($newName);
}
}
private function isClassAboutToBeDuplicated(string $newName): bool
{
return ClassExistenceStaticHelper::doesClassLikeExist($newName);
@ -316,33 +345,4 @@ final class ClassRenamer
return true;
}
/**
* @param string[] $oldToNewClasses
*/
private function renameClassImplements(ClassLike $classLike, array $oldToNewClasses): void
{
if (! $classLike instanceof Class_) {
return;
}
foreach ((array) $classLike->implements as $key => $implementName) {
if (! $implementName instanceof Name) {
continue;
}
if (! $implementName->getAttribute(AttributeKey::VIRTUAL_NODE)) {
continue;
}
$namespaceName = $classLike->getAttribute(AttributeKey::NAMESPACE_NAME);
$fullyQualifiedName = $namespaceName . '\\' . $implementName->toString();
$newName = $oldToNewClasses[$fullyQualifiedName] ?? null;
if ($newName === null) {
continue;
}
$classLike->implements[$key] = new FullyQualified($newName);
}
}
}

View File

@ -106,6 +106,23 @@ PHP
$this->oldToNewMethodsByClass = $configuration[self::OLD_TO_NEW_METHODS_BY_CLASS] ?? [];
}
/**
* @param MethodCall|StaticCall|ClassMethod $node
* @param string|string[] $newMethod
*/
private function skipClassMethod(Node $node, $newMethod, string $type): bool
{
if (! $node instanceof ClassMethod) {
return false;
}
if ($this->shouldSkipForAlreadyExistingClassMethod($node, $newMethod)) {
return true;
}
return $this->shouldSkipForExactClassMethodForClassMethod($node, $type);
}
/**
* @param MethodCall|StaticCall|ClassMethod $node
* @param string|mixed[] $newMethod
@ -150,23 +167,6 @@ PHP
return (bool) $classLike->getMethod($newMethod);
}
/**
* @param MethodCall|StaticCall|ClassMethod $node
* @param string|string[] $newMethod
*/
private function skipClassMethod(Node $node, $newMethod, string $type): bool
{
if (! $node instanceof ClassMethod) {
return false;
}
if ($this->shouldSkipForAlreadyExistingClassMethod($node, $newMethod)) {
return true;
}
return $this->shouldSkipForExactClassMethodForClassMethod($node, $type);
}
private function shouldSkipForExactClassMethodForClassMethod(ClassMethod $classMethod, string $type): bool
{
return $classMethod->getAttribute(AttributeKey::CLASS_NAME) === $type;

View File

@ -133,6 +133,17 @@ CODE_SAMPLE
]);
}
private function resolveClassFileLocation(string $implementedInterfaceName): string
{
$reflectionClass = new ReflectionClass($implementedInterfaceName);
$fileName = $reflectionClass->getFileName();
if (! $fileName) {
throw new ShouldNotHappenException();
}
return $fileName;
}
/**
* @return string[]
*/
@ -146,12 +157,14 @@ CODE_SAMPLE
);
}
private function getParentInterfaceIfFound(string $implementedInterfaceName): ?string
private function removeOrReplaceImlementedInterface(string $implementedInterfaceName, Class_ $class, int $key): void
{
$reflectionClass = new ReflectionClass($implementedInterfaceName);
// get first parent interface
return $reflectionClass->getInterfaceNames()[0] ?? null;
$parentInterface = $this->getParentInterfaceIfFound($implementedInterfaceName);
if ($parentInterface !== null) {
$class->implements[$key] = new FullyQualified($parentInterface);
} else {
unset($class->implements[$key]);
}
}
private function removeInterfaceFile(string $interfaceName, string $classFileLocation): void
@ -177,24 +190,11 @@ CODE_SAMPLE
$this->renamedClassesCollector->addClassRename($implementedInterfaceName, $className);
}
private function resolveClassFileLocation(string $implementedInterfaceName): string
private function getParentInterfaceIfFound(string $implementedInterfaceName): ?string
{
$reflectionClass = new ReflectionClass($implementedInterfaceName);
$fileName = $reflectionClass->getFileName();
if (! $fileName) {
throw new ShouldNotHappenException();
}
return $fileName;
}
private function removeOrReplaceImlementedInterface(string $implementedInterfaceName, Class_ $class, int $key): void
{
$parentInterface = $this->getParentInterfaceIfFound($implementedInterfaceName);
if ($parentInterface !== null) {
$class->implements[$key] = new FullyQualified($parentInterface);
} else {
unset($class->implements[$key]);
}
// get first parent interface
return $reflectionClass->getInterfaceNames()[0] ?? null;
}
}

View File

@ -121,32 +121,6 @@ PHP
return $this->isBooleanNot($classLike, $onlyPropertyProperty);
}
/**
* Matches:
* $this-><someProprety> === null
* null === $this-><someProprety>
*/
private function matchPropertyFetchNameComparedToNull(Node $node): ?string
{
if (! $node instanceof Identical && ! $node instanceof NotIdentical) {
return null;
}
if ($node->left instanceof PropertyFetch && $this->isNull($node->right)) {
$propertyFetch = $node->left;
} elseif ($node->right instanceof PropertyFetch && $this->isNull($node->left)) {
$propertyFetch = $node->right;
} else {
return null;
}
if (! $this->isName($propertyFetch->var, 'this')) {
return null;
}
return $this->getName($propertyFetch->name);
}
private function isIdenticalOrNotIdenticalToNull(Class_ $class, PropertyProperty $onlyPropertyProperty): bool
{
$isIdenticalOrNotIdenticalToNull = false;
@ -200,4 +174,30 @@ PHP
return $isBooleanNot;
}
/**
* Matches:
* $this-><someProprety> === null
* null === $this-><someProprety>
*/
private function matchPropertyFetchNameComparedToNull(Node $node): ?string
{
if (! $node instanceof Identical && ! $node instanceof NotIdentical) {
return null;
}
if ($node->left instanceof PropertyFetch && $this->isNull($node->right)) {
$propertyFetch = $node->left;
} elseif ($node->right instanceof PropertyFetch && $this->isNull($node->left)) {
$propertyFetch = $node->right;
} else {
return null;
}
if (! $this->isName($propertyFetch->var, 'this')) {
return null;
}
return $this->getName($propertyFetch->name);
}
}

View File

@ -74,17 +74,6 @@ final class TemplateGuesser
return $fullPath . $action . '.html.twig';
}
private function resolveController(string $class): string
{
$match = Strings::match($class, '#Controller\\\(.+)Controller$#');
if (! $match) {
return '';
}
$controller = Strings::replace($match[1], '#([a-z\d])([A-Z])#', '\\1_\\2');
return str_replace('\\', '/', $controller);
}
private function resolveBundle(string $class, string $namespace): string
{
$shortBundleClass = $this->bundleClassResolver->resolveShortBundleClassFromControllerClass($class);
@ -96,4 +85,15 @@ final class TemplateGuesser
$bundle = Strings::replace($bundle, '#Bundle$#');
return $bundle !== '' ? '@' . $bundle : '';
}
private function resolveController(string $class): string
{
$match = Strings::match($class, '#Controller\\\(.+)Controller$#');
if (! $match) {
return '';
}
$controller = Strings::replace($match[1], '#([a-z\d])([A-Z])#', '\\1_\\2');
return str_replace('\\', '/', $controller);
}
}

View File

@ -103,19 +103,6 @@ final class ThisRenderFactory
return $this->templateGuesser->resolveFromClassMethodNode($classMethod);
}
/**
* @param string[] $vars
*/
private function createArrayFromVars(array $vars): Array_
{
$arrayItems = [];
foreach ($vars as $var) {
$arrayItems[] = new ArrayItem(new Variable($var), new String_($var));
}
return new Array_($arrayItems);
}
private function resolveParametersExpr(
?Return_ $return,
SensioTemplateTagValueNode $sensioTemplateTagValueNode
@ -147,4 +134,17 @@ final class ThisRenderFactory
return null;
}
/**
* @param string[] $vars
*/
private function createArrayFromVars(array $vars): Array_
{
$arrayItems = [];
foreach ($vars as $var) {
$arrayItems[] = new ArrayItem(new Variable($var), new String_($var));
}
return new Array_($arrayItems);
}
}

View File

@ -183,32 +183,30 @@ PHP
}
}
private function processClassMethodWithoutReturn(
ClassMethod $classMethod,
MethodCall $thisRenderMethodCall
): void {
$classMethod->stmts[] = new Return_($thisRenderMethodCall);
}
/**
* This skips anonymous functions and functions, as their returns doesn't influence current code
*
* @param Node[] $stmts
* @return Return_[]
*/
private function findReturnsInCurrentScope(array $stmts): array
{
$returns = [];
$this->traverseNodesWithCallable($stmts, function (Node $node) use (&$returns) {
if ($node instanceof Closure || $node instanceof Function_) {
return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
private function processIsArrayOrResponseType(
Return_ $return,
Expr $returnExpr,
MethodCall $thisRenderMethodCall
): void {
$this->removeNode($return);
if (! $node instanceof Return_) {
return null;
}
// create instance of Response → return response, or return $this->render
$responseVariable = new Variable('responseOrData');
$returns[] = $node;
$assign = new Assign($responseVariable, $returnExpr);
return null;
});
$if = new If_(new Instanceof_($responseVariable, new FullyQualified(self::RESPONSE_CLASS)));
$if->stmts[] = new Return_($responseVariable);
$thisRenderMethodCall->args[1] = new Arg($responseVariable);
$returnThisRender = new Return_($thisRenderMethodCall);
$this->addNodesAfterNode([$assign, $if, $returnThisRender], $return);
return $returns;
}
private function hasLastReturnResponse(ClassMethod $classMethod): bool
@ -222,6 +220,32 @@ PHP
return $this->isReturnOfObjectType($lastReturn, self::RESPONSE_CLASS);
}
private function refactorReturn(
Return_ $return,
ClassMethod $classMethod,
SensioTemplateTagValueNode $sensioTemplateTagValueNode,
bool $hasThisRenderOrReturnsResponse
): void {
// nothing we can do
if ($return->expr === null) {
return;
}
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
$thisRenderMethodCall = $this->thisRenderFactory->create(
$classMethod,
$return,
$sensioTemplateTagValueNode
);
$this->refactorReturnWithValue(
$return,
$hasThisRenderOrReturnsResponse,
$thisRenderMethodCall,
$classMethod
);
}
private function refactorNoReturn(ClassMethod $classMethod, MethodCall $thisRenderMethodCall): void
{
$this->processClassMethodWithoutReturn($classMethod, $thisRenderMethodCall);
@ -266,55 +290,31 @@ PHP
$this->removePhpDocTagValueNode($classMethod, SensioTemplateTagValueNode::class);
}
private function refactorReturn(
Return_ $return,
private function processClassMethodWithoutReturn(
ClassMethod $classMethod,
SensioTemplateTagValueNode $sensioTemplateTagValueNode,
bool $hasThisRenderOrReturnsResponse
MethodCall $thisRenderMethodCall
): void {
// nothing we can do
if ($return->expr === null) {
return;
}
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
$thisRenderMethodCall = $this->thisRenderFactory->create(
$classMethod,
$return,
$sensioTemplateTagValueNode
);
$this->refactorReturnWithValue(
$return,
$hasThisRenderOrReturnsResponse,
$thisRenderMethodCall,
$classMethod
);
$classMethod->stmts[] = new Return_($thisRenderMethodCall);
}
/**
* This skips anonymous functions and functions, as their returns doesn't influence current code
*
* @param Node[] $stmts
* @return Return_[]
*/
private function findReturnsInCurrentScope(array $stmts): array
{
$returns = [];
$this->traverseNodesWithCallable($stmts, function (Node $node) use (&$returns) {
if ($node instanceof Closure || $node instanceof Function_) {
return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
private function processIsArrayOrResponseType(
Return_ $return,
Expr $returnExpr,
MethodCall $thisRenderMethodCall
): void {
$this->removeNode($return);
if (! $node instanceof Return_) {
return null;
}
// create instance of Response → return response, or return $this->render
$responseVariable = new Variable('responseOrData');
$returns[] = $node;
$assign = new Assign($responseVariable, $returnExpr);
return null;
});
$if = new If_(new Instanceof_($responseVariable, new FullyQualified(self::RESPONSE_CLASS)));
$if->stmts[] = new Return_($responseVariable);
return $returns;
$thisRenderMethodCall->args[1] = new Arg($responseVariable);
$returnThisRender = new Return_($thisRenderMethodCall);
$this->addNodesAfterNode([$assign, $if, $returnThisRender], $return);
}
}

View File

@ -98,6 +98,11 @@ final class ClassMethodNodeRemover
$this->removeParamFromAssign($classMethod, $paramName);
}
private function isParentConstructStaticCall(Node $node): bool
{
return $this->isStaticCallNamed($node, 'parent', MethodName::CONSTRUCT);
}
private function removeParamFromArgs(StaticCall $staticCall, string $paramName): void
{
foreach ($staticCall->args as $key => $arg) {
@ -144,9 +149,4 @@ final class ClassMethodNodeRemover
return $this->nodeNameResolver->isName($node->name, $method);
}
private function isParentConstructStaticCall(Node $node): bool
{
return $this->isStaticCallNamed($node, 'parent', MethodName::CONSTRUCT);
}
}

View File

@ -192,34 +192,6 @@ PHP
}
}
private function createConstantNameFromVariable(Variable $variable): string
{
$variableName = $this->getName($variable);
if ($variableName === null) {
throw new ShouldNotHappenException();
}
$constantName = StaticRectorStrings::camelCaseToUnderscore($variableName);
return strtoupper($constantName);
}
private function decorateWithVarAnnotation(ClassConst $classConst): void
{
$constStaticType = $this->getStaticType($classConst->consts[0]->value);
if ($constStaticType instanceof MixedType) {
return;
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $classConst->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($classConst);
}
$phpDocInfo->changeVarType($constStaticType);
}
private function createClassConst(Variable $variable, Expr $expr): ClassConst
{
$constantName = $this->createConstantNameFromVariable($variable);
@ -260,4 +232,32 @@ PHP
return $classConstFetch;
});
}
private function createConstantNameFromVariable(Variable $variable): string
{
$variableName = $this->getName($variable);
if ($variableName === null) {
throw new ShouldNotHappenException();
}
$constantName = StaticRectorStrings::camelCaseToUnderscore($variableName);
return strtoupper($constantName);
}
private function decorateWithVarAnnotation(ClassConst $classConst): void
{
$constStaticType = $this->getStaticType($classConst->consts[0]->value);
if ($constStaticType instanceof MixedType) {
return;
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $classConst->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($classConst);
}
$phpDocInfo->changeVarType($constStaticType);
}
}

View File

@ -234,38 +234,6 @@ PHP
return $objectTypes;
}
private function addInjectOrRequiredClassMethod(Class_ $class): void
{
/** @var string $className */
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
if (count($this->objectTypesToInject) === 0) {
return;
}
$injectClassMethod = $this->injectMethodFactory->createFromTypes(
$this->objectTypesToInject,
$className,
$this->framework
);
$this->classInsertManipulator->addAsFirstMethod($class, $injectClassMethod);
}
private function clearAbstractClassConstructor(ClassMethod $classMethod): void
{
foreach ($classMethod->getParams() as $key => $param) {
if (! $this->isObjectTypes($param, $this->objectTypesToInject)) {
continue;
}
unset($classMethod->params[$key]);
$this->classMethodNodeRemover->removeParamFromMethodBody($classMethod, $param);
}
$this->classMethodNodeRemover->removeClassMethodIfUseless($classMethod);
}
/**
* @param ObjectType[] $abstractClassConstructorParamTypes
*/
@ -292,6 +260,38 @@ PHP
}
}
private function clearAbstractClassConstructor(ClassMethod $classMethod): void
{
foreach ($classMethod->getParams() as $key => $param) {
if (! $this->isObjectTypes($param, $this->objectTypesToInject)) {
continue;
}
unset($classMethod->params[$key]);
$this->classMethodNodeRemover->removeParamFromMethodBody($classMethod, $param);
}
$this->classMethodNodeRemover->removeClassMethodIfUseless($classMethod);
}
private function addInjectOrRequiredClassMethod(Class_ $class): void
{
/** @var string $className */
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
if (count($this->objectTypesToInject) === 0) {
return;
}
$injectClassMethod = $this->injectMethodFactory->createFromTypes(
$this->objectTypesToInject,
$className,
$this->framework
);
$this->classInsertManipulator->addAsFirstMethod($class, $injectClassMethod);
}
private function popFirstObjectTypeFromUnionType(Type $paramType): Type
{
if (! $paramType instanceof UnionType) {

View File

@ -154,20 +154,6 @@ PHP
return $stringsToReplace;
}
/**
* @param string[] $stringsToReplace
*/
private function addClassConsts(array $stringsToReplace, Class_ $class): void
{
foreach ($stringsToReplace as $stringToReplace) {
$constantName = $this->createConstName($stringToReplace);
$classConst = $this->nodeFactory->createPrivateClassConst($constantName, new String_($stringToReplace));
$this->classInsertManipulator->addConstantToClass($class, $stringToReplace, $classConst);
}
}
private function replaceStringsWithClassConstReferences(Class_ $class, array $stringsToReplace): void
{
$this->traverseNodesWithCallable($class, function (Node $node) use ($stringsToReplace) {
@ -184,6 +170,20 @@ PHP
});
}
/**
* @param string[] $stringsToReplace
*/
private function addClassConsts(array $stringsToReplace, Class_ $class): void
{
foreach ($stringsToReplace as $stringToReplace) {
$constantName = $this->createConstName($stringToReplace);
$classConst = $this->nodeFactory->createPrivateClassConst($constantName, new String_($stringToReplace));
$this->classInsertManipulator->addConstantToClass($class, $stringToReplace, $classConst);
}
}
private function shouldSkipString(String_ $string): bool
{
$value = (string) $string->value;

View File

@ -175,15 +175,6 @@ PHP
$foreach->stmts[] = $nestedIfWithOnlyReturn;
}
private function negateOrDeNegate(Expr $expr): Expr
{
if ($expr instanceof BooleanNot) {
return $expr->expr;
}
return new BooleanNot($expr);
}
/**
* Matches:
* $a == 1 || $b == 1
@ -211,4 +202,13 @@ PHP
return $expr->right instanceof NotEqual;
}
private function negateOrDeNegate(Expr $expr): Expr
{
if ($expr instanceof BooleanNot) {
return $expr->expr;
}
return new BooleanNot($expr);
}
}

View File

@ -90,6 +90,19 @@ final class ReturnClosurePrinter
return $this->indentFluentCallToNewline($printedContent);
}
private function addUseStmts(string $useImport): void
{
$useBuilder = new UseBuilder($useImport);
$this->useStmts[] = $useBuilder->getNode();
}
private function createClosureParam(): Param
{
$paramBuilder = new ParamBuilder(self::CONTAINER_CONFIGURATOR);
$paramBuilder->setType('ContainerConfigurator');
return $paramBuilder->getNode();
}
/**
* @param mixed[] $services
* @return Expression[]
@ -113,17 +126,10 @@ final class ReturnClosurePrinter
return $stmts;
}
private function createClosureParam(): Param
private function indentFluentCallToNewline(string $content): string
{
$paramBuilder = new ParamBuilder(self::CONTAINER_CONFIGURATOR);
$paramBuilder->setType('ContainerConfigurator');
return $paramBuilder->getNode();
}
private function addUseStmts(string $useImport): void
{
$useBuilder = new UseBuilder($useImport);
$this->useStmts[] = $useBuilder->getNode();
$nextCallIndentReplacement = ')' . PHP_EOL . Strings::indent('->', 8, ' ');
return Strings::replace($content, '#\)->#', $nextCallIndentReplacement);
}
private function createServicesSetMethodCall(
@ -157,12 +163,6 @@ final class ReturnClosurePrinter
return $methodCall;
}
private function indentFluentCallToNewline(string $content): string
{
$nextCallIndentReplacement = ')' . PHP_EOL . Strings::indent('->', 8, ' ');
return Strings::replace($content, '#\)->#', $nextCallIndentReplacement);
}
/**
* @param mixed[]|mixed $value
*/

View File

@ -47,6 +47,22 @@ final class BuilderFormNodeFactory
return $classMethodBuilder->getNode();
}
private function createBuilderParam(): Param
{
$builderParamBuilder = new ParamBuilder('builder');
$builderParamBuilder->setType(new FullyQualified('Symfony\Component\Form\FormBuilderInterface'));
return $builderParamBuilder->getNode();
}
private function createOptionsParam(): Param
{
$optionsParamBuilder = new ParamBuilder('options');
$optionsParamBuilder->setType('array');
return $optionsParamBuilder->getNode();
}
/**
* @param Node[] $nodes
* @return Node[]
@ -79,20 +95,4 @@ final class BuilderFormNodeFactory
return $nodes;
}
private function createBuilderParam(): Param
{
$builderParamBuilder = new ParamBuilder('builder');
$builderParamBuilder->setType(new FullyQualified('Symfony\Component\Form\FormBuilderInterface'));
return $builderParamBuilder->getNode();
}
private function createOptionsParam(): Param
{
$optionsParamBuilder = new ParamBuilder('options');
$optionsParamBuilder->setType('array');
return $optionsParamBuilder->getNode();
}
}

View File

@ -39,11 +39,6 @@ final class ConfigureOptionsNodeFactory
return $methodBuilder->getNode();
}
private function createNull(): ConstFetch
{
return new ConstFetch(new Name('null'));
}
private function createParam(): Param
{
$paramBuilder = new ParamBuilder('resolver');
@ -65,4 +60,9 @@ final class ConfigureOptionsNodeFactory
return [new Arg($array)];
}
private function createNull(): ConstFetch
{
return new ConstFetch(new Name('null'));
}
}

View File

@ -91,6 +91,15 @@ PHP
return null;
}
private function isObjectMethodNameMatch(ClassMethod $classMethod): bool
{
if (! $this->isInObjectType($classMethod, 'Symfony\Component\Form\AbstractType')) {
return false;
}
return $this->isName($classMethod->name, 'getBlockPrefix');
}
/**
* return <$thisValue>;
*/
@ -107,13 +116,4 @@ PHP
return $onlyStmt->expr;
}
private function isObjectMethodNameMatch(ClassMethod $classMethod): bool
{
if (! $this->isInObjectType($classMethod, 'Symfony\Component\Form\AbstractType')) {
return false;
}
return $this->isName($classMethod->name, 'getBlockPrefix');
}
}

View File

@ -145,6 +145,28 @@ PHP
$this->to = $configuration[self::TO];
}
private function isKernelOrExtensionClass(Class_ $class): bool
{
if ($this->isObjectType($class, 'Symfony\Component\HttpKernel\DependencyInjection\Extension')) {
return true;
}
return $this->isObjectType($class, 'Symfony\Component\HttpKernel\Kernel');
}
private function validateConfiguration(string $from, string $to): void
{
if (! isset(self::FILE_LOADERS_BY_TYPE[$from])) {
$message = sprintf('File loader "%s" format is not supported', $from);
throw new InvalidConfigurationException($message);
}
if (! isset(self::FILE_LOADERS_BY_TYPE[$to])) {
$message = sprintf('File loader "%s" format is not supported', $to);
throw new InvalidConfigurationException($message);
}
}
private function refactorLoadMethodCall(Node $node): ?Node
{
if (! $node instanceof MethodCall) {
@ -182,26 +204,4 @@ PHP
return $node;
});
}
private function validateConfiguration(string $from, string $to): void
{
if (! isset(self::FILE_LOADERS_BY_TYPE[$from])) {
$message = sprintf('File loader "%s" format is not supported', $from);
throw new InvalidConfigurationException($message);
}
if (! isset(self::FILE_LOADERS_BY_TYPE[$to])) {
$message = sprintf('File loader "%s" format is not supported', $to);
throw new InvalidConfigurationException($message);
}
}
private function isKernelOrExtensionClass(Class_ $class): bool
{
if ($this->isObjectType($class, 'Symfony\Component\HttpKernel\DependencyInjection\Extension')) {
return true;
}
return $this->isObjectType($class, 'Symfony\Component\HttpKernel\Kernel');
}
}

View File

@ -154,6 +154,36 @@ PHP
return $methodCall;
}
private function refactorCollectionOptions(MethodCall $methodCall): void
{
$optionsArray = $this->matchOptionsArray($methodCall);
if ($optionsArray === null) {
return;
}
foreach ($optionsArray->items as $arrayItem) {
if ($arrayItem->key === null) {
continue;
}
if (! $this->isValues($arrayItem->key, ['entry', 'entry_type'])) {
continue;
}
if (! $arrayItem->value instanceof New_) {
continue;
}
$newClass = $arrayItem->value->class;
if (! $newClass instanceof Name) {
continue;
}
$arrayItem->value = $this->createClassConstantReference($newClass->toString());
}
}
/**
* @param Arg[] $argNodes
*/
@ -243,34 +273,4 @@ PHP
$class->stmts[] = $this->configureOptionsNodeFactory->create($namesToArgs);
}
private function refactorCollectionOptions(MethodCall $methodCall): void
{
$optionsArray = $this->matchOptionsArray($methodCall);
if ($optionsArray === null) {
return;
}
foreach ($optionsArray->items as $arrayItem) {
if ($arrayItem->key === null) {
continue;
}
if (! $this->isValues($arrayItem->key, ['entry', 'entry_type'])) {
continue;
}
if (! $arrayItem->value instanceof New_) {
continue;
}
$newClass = $arrayItem->value->class;
if (! $newClass instanceof Name) {
continue;
}
$arrayItem->value = $this->createClassConstantReference($newClass->toString());
}
}
}

View File

@ -130,6 +130,15 @@ PHP
$this->processPreviousAssign($node, $firstArgument);
}
/**
* @return string[]
*/
private function splitProcessCommandToItems(string $process): array
{
$privatesCaller = new PrivatesCaller();
return $privatesCaller->callPrivateMethod(new StringInput(''), 'tokenize', $process);
}
private function processPreviousAssign(Node $node, Node $firstArgument): void
{
/** @var Assign|null $previousNodeAssign */
@ -161,13 +170,4 @@ PHP
return $checkedNode;
});
}
/**
* @return string[]
*/
private function splitProcessCommandToItems(string $process): array
{
$privatesCaller = new PrivatesCaller();
return $privatesCaller->callPrivateMethod(new StringInput(''), 'tokenize', $process);
}
}

View File

@ -82,18 +82,6 @@ final class NonInformativeReturnTagRemover
$this->removeFullyQualifiedObjectType($returnType, $returnTagValueNode, $phpDocInfo);
}
/**
* @param string[] $values
*/
private function isIdentifierWithValues(TypeNode $typeNode, array $values): bool
{
if (! $typeNode instanceof IdentifierTypeNode) {
return false;
}
return in_array($typeNode->name, $values, true);
}
private function removeNonUniqueUselessDocNames(
Type $returnType,
ReturnTagValueNode $returnTagValueNode,
@ -113,29 +101,20 @@ final class NonInformativeReturnTagRemover
}
}
private function matchNullabledType(Type $returnType): ?Type
{
if (! $returnType instanceof UnionType) {
return null;
private function removeShortObjectType(
Type $returnType,
ReturnTagValueNode $returnTagValueNode,
?PhpDocInfo $phpDocInfo
): void {
if (! $returnType instanceof ShortenedObjectType) {
return;
}
if (! $returnType->isSuperTypeOf(new NullType())->yes()) {
return null;
if (! $this->isIdentifierWithValues($returnTagValueNode->type, [$returnType->getShortName()])) {
return;
}
if (count($returnType->getTypes()) !== 2) {
return null;
}
foreach ($returnType->getTypes() as $unionedReturnType) {
if ($unionedReturnType instanceof NullType) {
continue;
}
return $unionedReturnType;
}
return null;
$phpDocInfo->removeByType(ReturnTagValueNode::class);
}
private function removeNullableType(
@ -168,43 +147,6 @@ final class NonInformativeReturnTagRemover
$phpDocInfo->removeByType(ReturnTagValueNode::class);
}
private function matchNullabledReturnTagValueNode(ReturnTagValueNode $returnTagValueNode): ?TypeNode
{
if (! $returnTagValueNode->type instanceof UnionTypeNode) {
return null;
}
if (count($returnTagValueNode->type->types) !== 2) {
return null;
}
foreach ($returnTagValueNode->type->types as $unionedReturnTagValueNode) {
if ($this->isIdentifierWithValues($unionedReturnTagValueNode, ['null'])) {
continue;
}
return $unionedReturnTagValueNode;
}
return null;
}
private function removeShortObjectType(
Type $returnType,
ReturnTagValueNode $returnTagValueNode,
?PhpDocInfo $phpDocInfo
): void {
if (! $returnType instanceof ShortenedObjectType) {
return;
}
if (! $this->isIdentifierWithValues($returnTagValueNode->type, [$returnType->getShortName()])) {
return;
}
$phpDocInfo->removeByType(ReturnTagValueNode::class);
}
private function removeFullyQualifiedObjectType(
Type $returnType,
ReturnTagValueNode $returnTagValueNode,
@ -226,6 +168,64 @@ final class NonInformativeReturnTagRemover
}
}
/**
* @param string[] $values
*/
private function isIdentifierWithValues(TypeNode $typeNode, array $values): bool
{
if (! $typeNode instanceof IdentifierTypeNode) {
return false;
}
return in_array($typeNode->name, $values, true);
}
private function matchNullabledType(Type $returnType): ?Type
{
if (! $returnType instanceof UnionType) {
return null;
}
if (! $returnType->isSuperTypeOf(new NullType())->yes()) {
return null;
}
if (count($returnType->getTypes()) !== 2) {
return null;
}
foreach ($returnType->getTypes() as $unionedReturnType) {
if ($unionedReturnType instanceof NullType) {
continue;
}
return $unionedReturnType;
}
return null;
}
private function matchNullabledReturnTagValueNode(ReturnTagValueNode $returnTagValueNode): ?TypeNode
{
if (! $returnTagValueNode->type instanceof UnionTypeNode) {
return null;
}
if (count($returnTagValueNode->type->types) !== 2) {
return null;
}
foreach ($returnTagValueNode->type->types as $unionedReturnTagValueNode) {
if ($this->isIdentifierWithValues($unionedReturnTagValueNode, ['null'])) {
continue;
}
return $unionedReturnTagValueNode;
}
return null;
}
private function isClassNameAndPartMatch(string $className, string $returnTagValueNodeType): bool
{
if ($className === $returnTagValueNodeType) {

View File

@ -257,31 +257,6 @@ PHP
}
}
private function addReturnTypeToChildMethod(
ClassLike $classLike,
ClassMethod $classMethod,
Type $returnType
): void {
$methodName = $this->getName($classMethod);
$currentClassMethod = $classLike->getMethod($methodName);
if ($currentClassMethod === null) {
return;
}
$resolvedChildTypeNode = $this->resolveChildTypeNode($returnType);
if ($resolvedChildTypeNode === null) {
return;
}
$currentClassMethod->returnType = $resolvedChildTypeNode;
// make sure the type is not overridden
$currentClassMethod->returnType->setAttribute(AttributeKey::DO_NOT_CHANGE, true);
$this->notifyNodeFileInfo($currentClassMethod);
}
private function isVoidDueToThrow(Node $node, Node $inferredReturnNode): bool
{
if (! $inferredReturnNode instanceof Identifier) {
@ -323,4 +298,29 @@ PHP
return $inferedType->isSubTypeOf($currentType)->yes();
}
private function addReturnTypeToChildMethod(
ClassLike $classLike,
ClassMethod $classMethod,
Type $returnType
): void {
$methodName = $this->getName($classMethod);
$currentClassMethod = $classLike->getMethod($methodName);
if ($currentClassMethod === null) {
return;
}
$resolvedChildTypeNode = $this->resolveChildTypeNode($returnType);
if ($resolvedChildTypeNode === null) {
return;
}
$currentClassMethod->returnType = $resolvedChildTypeNode;
// make sure the type is not overridden
$currentClassMethod->returnType->setAttribute(AttributeKey::DO_NOT_CHANGE, true);
$this->notifyNodeFileInfo($currentClassMethod);
}
}

View File

@ -100,37 +100,6 @@ final class ConstructorPropertyTypeInferer extends AbstractTypeInferer implement
return $this->typeFactory->createMixedPassedOrUnionType($types);
}
private function resolveFullyQualifiedOrAliasedObjectType(Param $param): ?Type
{
if ($param->type === null) {
return null;
}
$fullyQualifiedName = $this->nodeNameResolver->getName($param->type);
if (! $fullyQualifiedName) {
return null;
}
$originalName = $param->type->getAttribute(AttributeKey::ORIGINAL_NAME);
if (! $originalName instanceof Name) {
return null;
}
// if the FQN has different ending than the original, it was aliased and we need to return the alias
if (! Strings::endsWith($fullyQualifiedName, '\\' . $originalName->toString())) {
$className = $originalName->toString();
if (class_exists($className)) {
return new FullyQualifiedObjectType($className);
}
// @note: $fullyQualifiedName is a guess, needs real life test
return new AliasedObjectType($originalName->toString(), $fullyQualifiedName);
}
return null;
}
private function resolveParamTypeToPHPStanType(Param $param): Type
{
if ($param->type === null) {
@ -195,4 +164,35 @@ final class ConstructorPropertyTypeInferer extends AbstractTypeInferer implement
return false;
}
private function resolveFullyQualifiedOrAliasedObjectType(Param $param): ?Type
{
if ($param->type === null) {
return null;
}
$fullyQualifiedName = $this->nodeNameResolver->getName($param->type);
if (! $fullyQualifiedName) {
return null;
}
$originalName = $param->type->getAttribute(AttributeKey::ORIGINAL_NAME);
if (! $originalName instanceof Name) {
return null;
}
// if the FQN has different ending than the original, it was aliased and we need to return the alias
if (! Strings::endsWith($fullyQualifiedName, '\\' . $originalName->toString())) {
$className = $originalName->toString();
if (class_exists($className)) {
return new FullyQualifiedObjectType($className);
}
// @note: $fullyQualifiedName is a guess, needs real life test
return new AliasedObjectType($originalName->toString(), $fullyQualifiedName);
}
return null;
}
}

View File

@ -171,21 +171,25 @@ final class RectorApplication
$this->eventDispatcher->dispatch(new AfterProcessEvent());
}
/**
* This prevent CI report flood with 1 file = 1 line in progress bar
*/
private function configureStepCount(SymfonyStyle $symfonyStyle): void
private function prepareProgressBar(int $fileCount): void
{
$privatesAccessor = new PrivatesAccessor();
/** @var ProgressBar $progressBar */
$progressBar = $privatesAccessor->getPrivateProperty($symfonyStyle, 'progressBar');
if ($progressBar->getMaxSteps() < 40) {
if ($this->symfonyStyle->isVerbose()) {
return;
}
$redrawFrequency = (int) ($progressBar->getMaxSteps() / 20);
$progressBar->setRedrawFrequency($redrawFrequency);
if (! $this->configuration->showProgressBar()) {
return;
}
// why 5? one for each cycle, so user sees some activity all the time
$stepMultiplier = 4;
if ($this->fileSystemFileProcessor->getFileSystemRectorsCount() !== 0) {
++$stepMultiplier;
}
$this->symfonyStyle->progressStart($fileCount * $stepMultiplier);
$this->configureStepCount($this->symfonyStyle);
}
/**
@ -201,66 +205,6 @@ final class RectorApplication
$this->nodeScopeResolver->setAnalysedFiles($filePaths);
}
private function tryCatchWrapper(SmartFileInfo $smartFileInfo, callable $callback, string $phase): void
{
$this->advance($smartFileInfo, $phase);
try {
if (in_array($smartFileInfo, $this->notParsedFiles, true)) {
// we cannot process this file
return;
}
$callback($smartFileInfo);
} catch (AnalysedCodeException $analysedCodeException) {
$this->notParsedFiles[] = $smartFileInfo;
$this->errorAndDiffCollector->addAutoloadError($analysedCodeException, $smartFileInfo);
} catch (Throwable $throwable) {
if ($this->symfonyStyle->isVerbose()) {
throw $throwable;
}
$this->errorAndDiffCollector->addThrowableWithFileInfo($throwable, $smartFileInfo);
}
}
private function printFileInfo(SmartFileInfo $fileInfo): void
{
if ($this->removedAndAddedFilesCollector->isFileRemoved($fileInfo)) {
// skip, because this file exists no more
return;
}
$oldContent = $fileInfo->getContents();
$newContent = $this->configuration->isDryRun() ? $this->fileProcessor->printToString($fileInfo)
: $this->fileProcessor->printToFile($fileInfo);
$this->errorAndDiffCollector->addFileDiff($fileInfo, $newContent, $oldContent);
}
private function advance(SmartFileInfo $smartFileInfo, string $phase): void
{
if ($this->symfonyStyle->isVerbose()) {
$relativeFilePath = $smartFileInfo->getRelativeFilePathFromDirectory(getcwd());
$message = sprintf('[%s] %s', $phase, $relativeFilePath);
$this->symfonyStyle->writeln($message);
} elseif ($this->configuration->showProgressBar()) {
$this->symfonyStyle->progressAdvance();
}
}
private function processFileSystemRectors(SmartFileInfo $smartFileInfo): void
{
if ($this->removedAndAddedFilesCollector->isFileRemoved($smartFileInfo)) {
// skip, because this file exists no more
return;
}
$this->fileSystemFileProcessor->processFileInfo($smartFileInfo);
}
/**
* @param SmartFileInfo[] $phpFileInfos
*/
@ -285,24 +229,80 @@ final class RectorApplication
}
}
private function prepareProgressBar(int $fileCount): void
private function tryCatchWrapper(SmartFileInfo $smartFileInfo, callable $callback, string $phase): void
{
$this->advance($smartFileInfo, $phase);
try {
if (in_array($smartFileInfo, $this->notParsedFiles, true)) {
// we cannot process this file
return;
}
$callback($smartFileInfo);
} catch (AnalysedCodeException $analysedCodeException) {
$this->notParsedFiles[] = $smartFileInfo;
$this->errorAndDiffCollector->addAutoloadError($analysedCodeException, $smartFileInfo);
} catch (Throwable $throwable) {
if ($this->symfonyStyle->isVerbose()) {
throw $throwable;
}
$this->errorAndDiffCollector->addThrowableWithFileInfo($throwable, $smartFileInfo);
}
}
private function processFileSystemRectors(SmartFileInfo $smartFileInfo): void
{
if ($this->removedAndAddedFilesCollector->isFileRemoved($smartFileInfo)) {
// skip, because this file exists no more
return;
}
$this->fileSystemFileProcessor->processFileInfo($smartFileInfo);
}
private function printFileInfo(SmartFileInfo $fileInfo): void
{
if ($this->removedAndAddedFilesCollector->isFileRemoved($fileInfo)) {
// skip, because this file exists no more
return;
}
$oldContent = $fileInfo->getContents();
$newContent = $this->configuration->isDryRun() ? $this->fileProcessor->printToString($fileInfo)
: $this->fileProcessor->printToFile($fileInfo);
$this->errorAndDiffCollector->addFileDiff($fileInfo, $newContent, $oldContent);
}
/**
* This prevent CI report flood with 1 file = 1 line in progress bar
*/
private function configureStepCount(SymfonyStyle $symfonyStyle): void
{
$privatesAccessor = new PrivatesAccessor();
/** @var ProgressBar $progressBar */
$progressBar = $privatesAccessor->getPrivateProperty($symfonyStyle, 'progressBar');
if ($progressBar->getMaxSteps() < 40) {
return;
}
$redrawFrequency = (int) ($progressBar->getMaxSteps() / 20);
$progressBar->setRedrawFrequency($redrawFrequency);
}
private function advance(SmartFileInfo $smartFileInfo, string $phase): void
{
if ($this->symfonyStyle->isVerbose()) {
return;
$relativeFilePath = $smartFileInfo->getRelativeFilePathFromDirectory(getcwd());
$message = sprintf('[%s] %s', $phase, $relativeFilePath);
$this->symfonyStyle->writeln($message);
} elseif ($this->configuration->showProgressBar()) {
$this->symfonyStyle->progressAdvance();
}
if (! $this->configuration->showProgressBar()) {
return;
}
// why 5? one for each cycle, so user sees some activity all the time
$stepMultiplier = 4;
if ($this->fileSystemFileProcessor->getFileSystemRectorsCount() !== 0) {
++$stepMultiplier;
}
$this->symfonyStyle->progressStart($fileCount * $stepMultiplier);
$this->configureStepCount($this->symfonyStyle);
}
}

View File

@ -87,6 +87,25 @@ final class ShowCommand extends AbstractCommand
return ShellCode::SUCCESS;
}
/**
* @param RectorInterface[] $rectors
* @return RectorInterface[]
*/
private function filterAndSortRectors(array $rectors): array
{
sort($rectors);
return array_filter($rectors, function (RectorInterface $rector) {
// utils rules
if ($rector instanceof ClassSyncerRectorInterface) {
return false;
}
// skip as internal and always run
return ! $rector instanceof PostRectorInterface;
});
}
/**
* Resolve configuration by convention
* @return mixed[]
@ -121,23 +140,4 @@ final class ShowCommand extends AbstractCommand
return $configuration;
}
/**
* @param RectorInterface[] $rectors
* @return RectorInterface[]
*/
private function filterAndSortRectors(array $rectors): array
{
sort($rectors);
return array_filter($rectors, function (RectorInterface $rector) {
// utils rules
if ($rector instanceof ClassSyncerRectorInterface) {
return false;
}
// skip as internal and always run
return ! $rector instanceof PostRectorInterface;
});
}
}

View File

@ -101,17 +101,6 @@ final class NonPhpFileProcessor
return $newContent;
}
/**
* @return string[]
*/
private function getOldToNewClasses(): array
{
return array_merge(
$this->changeConfiguration->getOldToNewClasses(),
$this->renamedClassesCollector->getOldToNewClasses()
);
}
private function reportFileContentChange(SmartFileInfo $neonYamlFileInfo, string $newContent): void
{
$relativeFilePathFromCwd = $neonYamlFileInfo->getRelativeFilePathFromCwd();
@ -127,4 +116,15 @@ final class NonPhpFileProcessor
$this->smartFileSystem->chmod($neonYamlFileInfo->getRealPath(), $neonYamlFileInfo->getPerms());
}
}
/**
* @return string[]
*/
private function getOldToNewClasses(): array
{
return array_merge(
$this->changeConfiguration->getOldToNewClasses(),
$this->renamedClassesCollector->getOldToNewClasses()
);
}
}

View File

@ -280,6 +280,20 @@ final class BetterNodeFinder
});
}
/**
* @param string[] $types
*/
private function isTypes(Node $node, array $types): bool
{
foreach ($types as $type) {
if (is_a($node, $type, true)) {
return true;
}
}
return false;
}
/**
* @param Node|Node[] $nodes
*/
@ -295,18 +309,4 @@ final class BetterNodeFinder
return null;
}
/**
* @param string[] $types
*/
private function isTypes(Node $node, array $types): bool
{
foreach ($types as $type) {
if (is_a($node, $type, true)) {
return true;
}
}
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More