mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 11:50:51 +00:00
[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:
parent
c7731e63e3
commit
411d76f9a9
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 \*\/$#", ' */');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[][]
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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*');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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*']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue
Block a user