apply privatization

This commit is contained in:
TomasVotruba 2020-03-29 00:06:05 +01:00
parent 91d1a1e397
commit 626287ec76
84 changed files with 867 additions and 850 deletions

View File

@ -9,6 +9,80 @@ PRs and issues are linked, so you can find more about it. Thanks to [ChangelogLi
<!-- changelog-linker -->
## Unreleased
### Added
- [#3080] [CodeQuality] Add ArrayKeysAndInArrayToIssetRector
- [#3070] [DeadCode] Add empty() + count($values) > 0 checks to RemoveUnusedNonEmptyArrayBeforeForeachRector
- [#3068] [DeadCode] Add RemoveAssignOfVoidReturnFunctionRector
- [#3062] [DeadCode] Add RemoveUnusedFunctionRector
- [#3066] [DeadCode] Add RemoveUnusedNonEmptyArrayBeforeForeachRector
- [#3047] [PHPUnit] Add CreateMockToCreateStubRector
- [#3081] [TypeDeclaration] Add class method param type resolving by property
- [#3058] [PHP 7.4] Add default null type on properties
- [#3059] [PHP 7.4] Add restoration null default only
- [#3057] [PHP 7.4] Add id tag support + remove array on collection property
- [#3078] Add Safe 0.7 set
- [#3072] [PHP 8.0] Add StrContainsRector
### Changed
- [#3082] [CodeQuality] use array_key_exists instead of isset
- [#3056] [PHP 7.4] Improve TypedPropertyRector for Doctrine collection
- [#3051] improve GeneratedValueTagValueNode
- [#3063] [PHP 5.5] Prevent error on non-string value in PregReplaceEModifierRector
- [#3040] Proofread docs, Thanks to [@greg0ire]
- [#3039] Proofread readme, Thanks to [@greg0ire]
- [#3083] use just one type of printing
### Fixed
- [#3050] Fix assert choice tag value node with class constant reference
- [#3049] fix union type on ReturnTypeDeclarationRector
- [#3052] fix content resolving
- [#3054] skip if not used with the `array []` operator fixes [#3053], Thanks to [@derflocki]
- [#3065] Fix multiple annotation reading of same type at class method
- [#3069] Fix Route separating key
- [#3077] Fix auto import
- [#3079] Fix annotation in requirements of [@Route]
- [#3064] [PHP 7.4] Fix ChangeReflectionTypeToStringToGetNameRector
### Removed
- [#3071] remove ctor dependency on property/assign removal
- [#3076] [PHP 8.0] drop preg_match support from StrContains, too vague
## [v0.7.7] - 2020-03-20
### Added
- [#3024] add DoctrineBehaviors 2.0
- [#3019] add fix for getIterator() on Finder for Array spread
- [#3034] Add checkstyle output format
- [#3021] add phpunit 9 rector to convert non-strict assertContains, Thanks to [@nightlinus]
- [#3023] add DoctrineBehaviors 2.0
### Changed
- [#3032] [DeadCode] Skip shifted variable
- [#3015] Rector CI is now exclusive for non-fork pushes + PRs, Thanks to [@JanMikes]
- [#3013] Commit rector processed changes from CI, Thanks to [@JanMikes]
- [#3036] Run cs after rector ci, Thanks to [@JanMikes]
- [#3027] ForToForeachRector fixture, Thanks to [@crishoj]
### Fixed
- [#3029] Fix other loop
- [#3022] PHP 7.4 deprecation fix, Thanks to [@alexeyshockov]
- [#3030] fix no-space change reprint in case of dual comment
- [#3035] Fix typo in README.md, Thanks to [@pgrimaud]
- [#3031] fix asterisk indent
### Removed
- [#3016] Delete DogFoodClass, Thanks to [@JanMikes]
## [v0.7.4] - 2020-03-11
### Added
@ -2650,3 +2724,59 @@ PRs and issues are linked, so you can find more about it. Thanks to [ChangelogLi
[#2982]: https://github.com/rectorphp/rector/pull/2982
[#2981]: https://github.com/rectorphp/rector/pull/2981
[#2980]: https://github.com/rectorphp/rector/pull/2980
[#3083]: https://github.com/rectorphp/rector/pull/3083
[#3082]: https://github.com/rectorphp/rector/pull/3082
[#3081]: https://github.com/rectorphp/rector/pull/3081
[#3080]: https://github.com/rectorphp/rector/pull/3080
[#3079]: https://github.com/rectorphp/rector/pull/3079
[#3078]: https://github.com/rectorphp/rector/pull/3078
[#3077]: https://github.com/rectorphp/rector/pull/3077
[#3076]: https://github.com/rectorphp/rector/pull/3076
[#3072]: https://github.com/rectorphp/rector/pull/3072
[#3071]: https://github.com/rectorphp/rector/pull/3071
[#3070]: https://github.com/rectorphp/rector/pull/3070
[#3069]: https://github.com/rectorphp/rector/pull/3069
[#3068]: https://github.com/rectorphp/rector/pull/3068
[#3066]: https://github.com/rectorphp/rector/pull/3066
[#3065]: https://github.com/rectorphp/rector/pull/3065
[#3064]: https://github.com/rectorphp/rector/pull/3064
[#3063]: https://github.com/rectorphp/rector/pull/3063
[#3062]: https://github.com/rectorphp/rector/pull/3062
[#3059]: https://github.com/rectorphp/rector/pull/3059
[#3058]: https://github.com/rectorphp/rector/pull/3058
[#3057]: https://github.com/rectorphp/rector/pull/3057
[#3056]: https://github.com/rectorphp/rector/pull/3056
[#3054]: https://github.com/rectorphp/rector/pull/3054
[#3053]: https://github.com/rectorphp/rector/pull/3053
[#3052]: https://github.com/rectorphp/rector/pull/3052
[#3051]: https://github.com/rectorphp/rector/pull/3051
[#3050]: https://github.com/rectorphp/rector/pull/3050
[#3049]: https://github.com/rectorphp/rector/pull/3049
[#3047]: https://github.com/rectorphp/rector/pull/3047
[#3040]: https://github.com/rectorphp/rector/pull/3040
[#3039]: https://github.com/rectorphp/rector/pull/3039
[#3036]: https://github.com/rectorphp/rector/pull/3036
[#3035]: https://github.com/rectorphp/rector/pull/3035
[#3034]: https://github.com/rectorphp/rector/pull/3034
[#3032]: https://github.com/rectorphp/rector/pull/3032
[#3031]: https://github.com/rectorphp/rector/pull/3031
[#3030]: https://github.com/rectorphp/rector/pull/3030
[#3029]: https://github.com/rectorphp/rector/pull/3029
[#3027]: https://github.com/rectorphp/rector/pull/3027
[#3024]: https://github.com/rectorphp/rector/pull/3024
[#3023]: https://github.com/rectorphp/rector/pull/3023
[#3022]: https://github.com/rectorphp/rector/pull/3022
[#3021]: https://github.com/rectorphp/rector/pull/3021
[#3019]: https://github.com/rectorphp/rector/pull/3019
[#3016]: https://github.com/rectorphp/rector/pull/3016
[#3015]: https://github.com/rectorphp/rector/pull/3015
[#3013]: https://github.com/rectorphp/rector/pull/3013
[v0.7.7]: https://github.com/rectorphp/rector/compare/v0.7.4...v0.7.7
[v0.7.4]: https://github.com/rectorphp/rector/compare/v0.7.3...v0.7.4
[@pgrimaud]: https://github.com/pgrimaud
[@nightlinus]: https://github.com/nightlinus
[@greg0ire]: https://github.com/greg0ire
[@derflocki]: https://github.com/derflocki
[@crishoj]: https://github.com/crishoj
[@alexeyshockov]: https://github.com/alexeyshockov
[@Route]: https://github.com/Route

View File

@ -50,7 +50,6 @@
},
"autoload": {
"psr-4": {
"Rector\\Core\\": "src",
"Rector\\Architecture\\": "rules/architecture/src",
"Rector\\AttributeAwarePhpDoc\\": "packages/attribute-aware-php-doc/src",
"Rector\\Autodiscovery\\": "rules/autodiscovery/src",
@ -58,10 +57,11 @@
"Rector\\CakePHPToSymfony\\": "rules/cakephp-to-symfony/src",
"Rector\\CakePHP\\": "rules/cakephp/src",
"Rector\\Celebrity\\": "rules/celebrity/src",
"Rector\\ChangesReporting\\": "packages/changes-reporting/src",
"Rector\\CodeQuality\\": "rules/code-quality/src",
"Rector\\CodingStyle\\": "rules/coding-style/src",
"Rector\\ConsoleDiffer\\": "packages/console-differ/src",
"Rector\\ChangesReporting\\": "packages/changes-reporting/src",
"Rector\\Core\\": "src",
"Rector\\DeadCode\\": "rules/dead-code/src",
"Rector\\DoctrineCodeQuality\\": "rules/doctrine-code-quality/src",
"Rector\\DoctrineGedmoToKnplabs\\": "rules/doctrine-gedmo-to-knplabs/src",
@ -103,6 +103,7 @@
"Rector\\PhpDeglobalize\\": "rules/php-deglobalize/src",
"Rector\\PhpSpecToPHPUnit\\": "rules/php-spec-to-phpunit/src",
"Rector\\Polyfill\\": "packages/polyfill/src",
"Rector\\Privatization\\": "rules/privatization/src",
"Rector\\RectorGenerator\\": "packages/rector-generator/src",
"Rector\\Refactoring\\": "packages/refactoring/src",
"Rector\\RemovingStatic\\": "rules/removing-static/src",
@ -126,7 +127,6 @@
},
"autoload-dev": {
"psr-4": {
"Rector\\Core\\Tests\\": "tests",
"Rector\\Architecture\\Tests\\": "rules/architecture/tests",
"Rector\\Autodiscovery\\Tests\\": "rules/autodiscovery/tests",
"Rector\\BetterPhpDocParser\\Tests\\": "packages/better-php-doc-parser/tests",
@ -135,6 +135,7 @@
"Rector\\Celebrity\\Tests\\": "rules/celebrity/tests",
"Rector\\CodeQuality\\Tests\\": "rules/code-quality/tests",
"Rector\\CodingStyle\\Tests\\": "rules/coding-style/tests",
"Rector\\Core\\Tests\\": "tests",
"Rector\\DeadCode\\Tests\\": "rules/dead-code/tests",
"Rector\\DoctrineCodeQuality\\Tests\\": "rules/doctrine-code-quality/tests",
"Rector\\DoctrineGedmoToKnplabs\\Tests\\": "rules/doctrine-gedmo-to-knplabs/tests",
@ -173,6 +174,7 @@
"Rector\\PhpDeglobalize\\Tests\\": "rules/php-deglobalize/tests",
"Rector\\PhpSpecToPHPUnit\\Tests\\": "rules/php-spec-to-phpunit/tests",
"Rector\\Polyfill\\Tests\\": "packages/polyfill/tests",
"Rector\\Privatization\\Tests\\": "rules/privatization/tests",
"Rector\\RemovingStatic\\Tests\\": "rules/removing-static/tests",
"Rector\\Renaming\\Tests\\": "rules/renaming/tests",
"Rector\\Restoration\\Tests\\": "rules/restoration/tests",

View File

@ -1,4 +1,4 @@
# All 472 Rectors Overview
# All 473 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -48,6 +48,7 @@
- [PhpDeglobalize](#phpdeglobalize)
- [PhpSpecToPHPUnit](#phpspectophpunit)
- [Polyfill](#polyfill)
- [Privatization](#privatization)
- [Refactoring](#refactoring)
- [RemovingStatic](#removingstatic)
- [Renaming](#renaming)
@ -4807,30 +4808,48 @@ Change Form that extends Control to Controller and decoupled FormType
-class SomeForm extends Control
+class SomeFormController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
- public function createComponentForm()
+ /**
+ * @Route(...)
+ */
+ public function actionSomeForm(\Symfony\Component\HttpFoundation\Request $request): \Symfony\Component\HttpFoundation\Response
{
- $form = new Form();
- $form->addText('name', 'Your name');
+ $form = $this->createForm(SomeFormType::class);
+ $form->handleRequest($request);
- public function createComponentForm()
+ /**
+ * @Route(...)
+ */
+ public function actionSomeForm(\Symfony\Component\HttpFoundation\Request $request): \Symfony\Component\HttpFoundation\Response
{
- $form = new Form();
- $form->addText('name', 'Your name');
+ $form = $this->createForm(SomeFormType::class);
+ $form->handleRequest($request);
- $form->onSuccess[] = [$this, 'processForm'];
- }
- $form->onSuccess[] = [$this, 'processForm'];
- }
-
- public function processForm(Form $form)
- {
- public function processForm(Form $form)
- {
- // process me
+ if ($form->isSuccess() && $form->isValid()) {
+ // process me
+ }
}
+ if ($form->isSuccess() && $form->isValid()) {
+ // process me
+ }
}
}
```
**New file**
```php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class SomeFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $formBuilder, array $options)
{
$formBuilder->add('name', TextType::class, [
'label' => 'Your name'
]);
}
}
```
<br>
### `FromHttpRequestGetHeaderToHeadersGetRector`
@ -7813,6 +7832,35 @@ Remove php version checks if they are passed
<br>
## Privatization
### `PrivatizeLocalOnlyMethodRector`
- class: [`Rector\Privatization\Rector\ClassMethod\PrivatizeLocalOnlyMethodRector`](/../master/rules/privatization/src/Rector/ClassMethod/PrivatizeLocalOnlyMethodRector.php)
- [test fixtures](/../master/rules/privatization/tests/Rector/ClassMethod/PrivatizeLocalOnlyMethodRector/Fixture)
Privatize local-only use methods
```diff
class SomeClass
{
/**
* @api
*/
public function run()
{
return $this->useMe();
}
- public function useMe()
+ private function useMe()
{
}
}
```
<br>
## Refactoring
### `MoveAndRenameClassRector`

View File

@ -143,19 +143,6 @@ final class PhpDocInfo
return $this->getPhpDocNode()->getVarTagValues()[0] ?? null;
}
public function getReturnTagValue(): ?AttributeAwareReturnTagValueNode
{
return $this->getPhpDocNode()->getReturnTagValues()[0] ?? null;
}
/**
* @return AttributeAwareParamTagValueNode[]
*/
public function getParamTagValues(): array
{
return $this->getPhpDocNode()->getParamTagValues();
}
/**
* @return PhpDocTagNode[]
*/
@ -402,11 +389,6 @@ final class PhpDocInfo
$this->addPhpDocTagNode($phpDocTagNode);
}
public function isEmpty(): bool
{
return $this->phpDocNode->children === [];
}
public function addTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): void
{
$name = $this->resolveNameForPhpDocTagValueNode($phpDocTagValueNode);
@ -440,9 +422,17 @@ final class PhpDocInfo
$this->addTagValueNode($paramTagValueNode);
}
public function getNode(): Node
private function getReturnTagValue(): ?AttributeAwareReturnTagValueNode
{
return $this->node;
return $this->getPhpDocNode()->getReturnTagValues()[0] ?? null;
}
/**
* @return AttributeAwareParamTagValueNode[]
*/
private function getParamTagValues(): array
{
return $this->getPhpDocNode()->getParamTagValues();
}
private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode

View File

@ -75,29 +75,6 @@ final class PhpDocInfoFactory
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory;
}
public function createFromString(Node $node, string $content): PhpDocInfo
{
$tokens = $this->lexer->tokenize($content);
$phpDocNode = $this->parseTokensToPhpDocNode($tokens);
/** @var AttributeAwarePhpDocNode $phpDocNode */
$phpDocNode = $this->attributeAwareNodeFactory->createFromNode($phpDocNode, $content);
$phpDocInfo = new PhpDocInfo(
$phpDocNode,
$tokens,
$content,
$this->staticTypeMapper,
$node,
$this->typeComparator,
$this->paramPhpDocNodeFactory
);
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
return $phpDocInfo;
}
public function createFromNode(Node $node): ?PhpDocInfo
{
/** needed for @see PhpDocNodeFactoryInterface */

View File

@ -132,10 +132,26 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
);
}
protected function resolveOriginalContentSpacingAndOrder(?string $originalContent): void
{
if ($originalContent === null) {
return;
}
$this->originalContent = $originalContent;
$this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent);
$this->hasNewlineAfterOpening = (bool) Strings::match($originalContent, '#^(\(\s+|\n)#m');
$this->hasNewlineBeforeClosing = (bool) Strings::match($originalContent, '#(\s+\)|\n(\s+)?)$#m');
$this->hasOpeningBracket = (bool) Strings::match($originalContent, '#^\(#');
$this->hasClosingBracket = (bool) Strings::match($originalContent, '#\)$#');
}
/**
* @param PhpDocTagValueNode[] $tagValueNodes
*/
protected function printTagValueNodesSeparatedByComma(array $tagValueNodes): string
private function printTagValueNodesSeparatedByComma(array $tagValueNodes): string
{
if ($tagValueNodes === []) {
return '';
@ -155,20 +171,4 @@ abstract class AbstractTagValueNode implements AttributeAwareNodeInterface, PhpD
return implode(', ', $itemsAsStrings);
}
protected function resolveOriginalContentSpacingAndOrder(?string $originalContent): void
{
if ($originalContent === null) {
return;
}
$this->originalContent = $originalContent;
$this->orderedVisibleItems = ArrayItemStaticHelper::resolveAnnotationItemsOrder($originalContent);
$this->hasNewlineAfterOpening = (bool) Strings::match($originalContent, '#^(\(\s+|\n)#m');
$this->hasNewlineBeforeClosing = (bool) Strings::match($originalContent, '#(\s+\)|\n(\s+)?)$#m');
$this->hasOpeningBracket = (bool) Strings::match($originalContent, '#^\(#');
$this->hasClosingBracket = (bool) Strings::match($originalContent, '#\)$#');
}
}

View File

@ -143,11 +143,6 @@ final class TableTagValueNode extends AbstractDoctrineTagValueNode
return $this->printContentItems($contentItems);
}
public function getName(): ?string
{
return $this->name;
}
public function getShortName(): string
{
return '@ORM\Table';

View File

@ -148,11 +148,6 @@ final class ColumnTagValueNode extends AbstractDoctrineTagValueNode
$this->type = $type;
}
public function changeUnique(bool $unique): void
{
$this->unique = $unique;
}
public function getType(): ?string
{
return $this->type;

View File

@ -22,9 +22,4 @@ final class TreeTagValueNode extends AbstractTagValueNode
{
return sprintf('(type="%s")', $this->type);
}
public function getType(): string
{
return $this->type;
}
}

View File

@ -43,7 +43,26 @@ final class MultilineSpaceFormatPreserver
return null;
}
public function setNewTextToPhpDocNode(
/**
* Fix multiline BC break - https://github.com/phpstan/phpdoc-parser/pull/26/files
*/
public function fixMultilineDescriptions(
AttributeAwareNodeInterface $attributeAwareNode
): AttributeAwareNodeInterface {
if (! $attributeAwareNode->getAttribute(Attribute::ORIGINAL_CONTENT)) {
return $attributeAwareNode;
}
$nodeWithRestoredSpaces = $this->restoreOriginalSpacingInText($attributeAwareNode);
if ($nodeWithRestoredSpaces !== null) {
$attributeAwareNode = $nodeWithRestoredSpaces;
$attributeAwareNode->setAttribute(Attribute::HAS_DESCRIPTION_WITH_ORIGINAL_SPACES, true);
}
return $attributeAwareNode;
}
private function setNewTextToPhpDocNode(
AttributeAwareNodeInterface $attributeAwareNode,
string $newText
): AttributeAwareNodeInterface {
@ -65,25 +84,6 @@ final class MultilineSpaceFormatPreserver
return $attributeAwareNode;
}
/**
* Fix multiline BC break - https://github.com/phpstan/phpdoc-parser/pull/26/files
*/
public function fixMultilineDescriptions(
AttributeAwareNodeInterface $attributeAwareNode
): AttributeAwareNodeInterface {
if (! $attributeAwareNode->getAttribute(Attribute::ORIGINAL_CONTENT)) {
return $attributeAwareNode;
}
$nodeWithRestoredSpaces = $this->restoreOriginalSpacingInText($attributeAwareNode);
if ($nodeWithRestoredSpaces !== null) {
$attributeAwareNode = $nodeWithRestoredSpaces;
$attributeAwareNode->setAttribute(Attribute::HAS_DESCRIPTION_WITH_ORIGINAL_SPACES, true);
}
return $attributeAwareNode;
}
/**
* @param PhpDocTextNode|AttributeAwareNodeInterface $attributeAwareNode
*/

View File

@ -67,11 +67,6 @@ final class ErrorAndDiffCollector
$this->nodeRemovingCommander = $nodeRemovingCommander;
}
public function addError(Error $error): void
{
$this->errors[] = $error;
}
/**
* @return Error[]
*/
@ -132,7 +127,7 @@ final class ErrorAndDiffCollector
{
$message = $this->exceptionCorrector->getAutoloadExceptionMessageAndAddLocation($analysedCodeException);
$this->addError(new Error($fileInfo, $message));
$this->errors[] = new Error($fileInfo, $message);
}
public function addErrorWithRectorClassMessageAndFileInfo(
@ -149,8 +144,7 @@ final class ErrorAndDiffCollector
if ($rectorClass) {
$this->addErrorWithRectorClassMessageAndFileInfo($rectorClass, $throwable->getMessage(), $fileInfo);
} else {
$error = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
$this->addError($error);
$this->errors[] = new Error($fileInfo, $throwable->getMessage(), $throwable->getCode());
}
}
}

View File

@ -29,15 +29,6 @@ final class RectorChangeCollector
$this->currentRectorProvider = $currentRectorProvider;
}
public function addRectorClassWithLine(RectorInterface $rector, SmartFileInfo $smartFileInfo, int $line): void
{
$this->rectorWithFileAndLineChanges[] = new RectorWithFileAndLineChange(
$rector,
$smartFileInfo->getRealPath(),
$line
);
}
/**
* @return RectorWithFileAndLineChange[]
*/
@ -67,4 +58,13 @@ final class RectorChangeCollector
$this->addRectorClassWithLine($currentRector, $fileInfo, $node->getLine());
}
private function addRectorClassWithLine(RectorInterface $rector, SmartFileInfo $smartFileInfo, int $line): void
{
$this->rectorWithFileAndLineChanges[] = new RectorWithFileAndLineChange(
$rector,
$smartFileInfo->getRealPath(),
$line
);
}
}

View File

@ -30,11 +30,6 @@ final class RectorWithFileAndLineChange
$this->realPath = $realPath;
}
public function getRector(): RectorInterface
{
return $this->rector;
}
public function getRectorDefinitionsDescription(): string
{
return $this->rector->getDefinition()->getDescription();

View File

@ -22,6 +22,9 @@ final class CompleteUnifiedDiffOutputBuilderFactory
$this->privatesAccessor = new PrivatesAccessor();
}
/**
* @api
*/
public function create(): UnifiedDiffOutputBuilder
{
$unifiedDiffOutputBuilder = new UnifiedDiffOutputBuilder('');

View File

@ -194,7 +194,7 @@ abstract class AbstractFileSystemRector implements FileSystemRectorInterface
$this->removedAndAddedFilesCollector->removeFile($smartFileInfo);
}
protected function addFile(string $filePath, string $content): void
private function addFile(string $filePath, string $content): void
{
$this->removedAndAddedFilesCollector->addFileWithContent($filePath, $content);
}

View File

@ -2,6 +2,9 @@ services:
_defaults:
public: true
autowire: true
autoconfigure: true
Rector\NodeCollector\:
resource: '../src'
exclude:
- '../src/ValueObject/*'

View File

@ -18,6 +18,7 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
@ -49,7 +50,7 @@ final class ParsedFunctionLikeNodeCollector
/**
* E.g. [$this, 'someLocalMethod']
* @var Array_[][][]
* @var ArrayCallable[][][]
*/
private $arrayCallablesByTypeAndMethod = [];
@ -96,7 +97,9 @@ final class ParsedFunctionLikeNodeCollector
return;
}
$this->arrayCallablesByTypeAndMethod[$className][$methodName][] = $node;
$arrayCallable = new ArrayCallable($className, $methodName);
$this->arrayCallablesByTypeAndMethod[$className][$methodName][] = $arrayCallable;
return;
}
@ -129,7 +132,7 @@ final class ParsedFunctionLikeNodeCollector
}
/**
* @return MethodCall[]|StaticCall[]|Array_[]
* @return MethodCall[]|StaticCall[]|ArrayCallable[]
*/
public function findByClassAndMethod(string $className, string $methodName): array
{
@ -215,30 +218,23 @@ final class ParsedFunctionLikeNodeCollector
private function addCall(Node $node): void
{
// one node can be of multiple-class types
$classType = $this->resolveClassType($node);
if ($node instanceof MethodCall) {
$classType = $this->resolveNodeClassTypes($node->var);
} else {
/** @var StaticCall $node */
$classType = $this->resolveNodeClassTypes($node->class);
}
$methodName = $this->nodeNameResolver->getName($node->name);
if ($classType instanceof MixedType) { // anonymous
return;
}
$methodName = $this->nodeNameResolver->getName($node->name);
if ($methodName === null) {
return;
}
if ($classType instanceof ObjectType) {
$this->methodsCallsByTypeAndMethod[$classType->getClassName()][$methodName][] = $node;
}
if ($classType instanceof UnionType) {
foreach ($classType->getTypes() as $unionedType) {
if (! $unionedType instanceof ObjectType) {
continue;
}
$this->methodsCallsByTypeAndMethod[$unionedType->getClassName()][$methodName][] = $node;
}
}
$this->addCallByType($node, $classType, $methodName);
}
private function isThisVariable(Node $node): bool
@ -291,19 +287,20 @@ final class ParsedFunctionLikeNodeCollector
return $this->nodeTypeResolver->resolve($node);
}
/**
* @param MethodCall|StaticCall $node
*/
private function resolveClassType(Node $node): Type
private function addCallByType(Node $node, Type $classType, string $methodName): void
{
if ($node instanceof MethodCall) {
if ($node->var instanceof MethodCall) {
return $this->resolveNodeClassTypes($node);
}
return $this->resolveNodeClassTypes($node->var);
if ($classType instanceof ObjectType) {
$this->methodsCallsByTypeAndMethod[$classType->getClassName()][$methodName][] = $node;
}
return $this->resolveNodeClassTypes($node->class);
if ($classType instanceof UnionType) {
foreach ($classType->getTypes() as $unionedType) {
if (! $unionedType instanceof ObjectType) {
continue;
}
$this->methodsCallsByTypeAndMethod[$unionedType->getClassName()][$methodName][] = $node;
}
}
}
}

View File

@ -105,10 +105,20 @@ final class ClassLikeParsedNodesFinder
return array_merge($this->findChildrenOfClass($type), $this->findImplementersOfInterface($type));
}
public function findInterface(string $class): ?Interface_
{
return $this->parsedNodeCollector->findInterface($class);
}
public function findClass(string $name): ?Class_
{
return $this->parsedNodeCollector->findClass($name);
}
/**
* @return Interface_[]
*/
public function findImplementersOfInterface(string $interface): array
private function findImplementersOfInterface(string $interface): array
{
$implementerInterfaces = [];
@ -125,16 +135,6 @@ final class ClassLikeParsedNodesFinder
return $implementerInterfaces;
}
public function findInterface(string $class): ?Interface_
{
return $this->parsedNodeCollector->findInterface($class);
}
public function findClass(string $name): ?Class_
{
return $this->parsedNodeCollector->findClass($name);
}
private function isChildOrEqualClassLike(string $desiredClass, ?string $currentClassName): bool
{
if ($currentClassName === null) {

View File

@ -4,8 +4,6 @@ declare(strict_types=1);
namespace Rector\NodeCollector\NodeFinder;
use Nette\Utils\Strings;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
@ -15,7 +13,6 @@ use Rector\NodeCollector\NodeCollector\ParsedFunctionLikeNodeCollector;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use ReflectionClass;
final class FunctionLikeParsedNodesFinder
{
@ -90,59 +87,6 @@ final class FunctionLikeParsedNodesFinder
return $this->parsedFunctionLikeNodeCollector->findMethod($className, $methodName);
}
/**
* @todo deocpule
*/
public function isStaticMethod(string $methodName, string $className): bool
{
$methodNode = $this->findMethod($methodName, $className);
if ($methodNode !== null) {
return $methodNode->isStatic();
}
// could be static in doc type magic
// @see https://regex101.com/r/tlvfTB/1
if (class_exists($className) || trait_exists($className)) {
$reflectionClass = new ReflectionClass($className);
if (Strings::match(
(string) $reflectionClass->getDocComment(),
'#@method\s*static\s*(.*?)\b' . $methodName . '\b#'
)) {
return true;
}
// probably magic method → we don't know
if (! method_exists($className, $methodName)) {
return false;
}
$methodReflection = $reflectionClass->getMethod($methodName);
return $methodReflection->isStatic();
}
return false;
}
/**
* @return MethodCall[]|StaticCall[]|Array_[]
*/
public function findClassMethodCalls(ClassMethod $classMethod): array
{
/** @var string|null $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) { // anonymous
return [];
}
/** @var string|null $methodName */
$methodName = $this->nodeNameResolver->getName($classMethod);
if ($methodName === null) {
return [];
}
return $this->parsedFunctionLikeNodeCollector->findByClassAndMethod($className, $methodName);
}
/**
* @return MethodCall[][]|StaticCall[][]
*/

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Rector\NodeCollector\NodeFinder;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeCollector\NodeCollector\ParsedFunctionLikeNodeCollector;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class MethodCallParsedNodesFinder
{
/**
* @var ParsedFunctionLikeNodeCollector
*/
private $parsedFunctionLikeNodeCollector;
public function __construct(ParsedFunctionLikeNodeCollector $parsedFunctionLikeNodeCollector)
{
$this->parsedFunctionLikeNodeCollector = $parsedFunctionLikeNodeCollector;
}
/**
* @return MethodCall[]|StaticCall[]|ArrayCallable[]
*/
public function findClassMethodCalls(ClassMethod $classMethod): array
{
/** @var string|null $className */
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) { // anonymous
return [];
}
/** @var string $methodName */
$methodName = $classMethod->getAttribute(AttributeKey::METHOD_NAME);
return $this->parsedFunctionLikeNodeCollector->findByClassAndMethod($className, $methodName);
}
}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Rector\NodeCollector;
use Nette\Utils\Strings;
use Rector\NodeCollector\NodeCollector\ParsedFunctionLikeNodeCollector;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use ReflectionClass;
final class StaticAnalyzer
{
/**
* @var ParsedFunctionLikeNodeCollector
*/
private $parsedFunctionLikeNodeCollector;
public function __construct(ParsedFunctionLikeNodeCollector $parsedFunctionLikeNodeCollector)
{
$this->parsedFunctionLikeNodeCollector = $parsedFunctionLikeNodeCollector;
}
public function isStaticMethod(string $methodName, string $className): bool
{
$methodNode = $this->parsedFunctionLikeNodeCollector->findMethod($methodName, $className);
if ($methodNode !== null) {
return $methodNode->isStatic();
}
// could be static in doc type magic
// @see https://regex101.com/r/tlvfTB/1
if (! ClassExistenceStaticHelper::doesClassLikeExist($className)) {
return false;
}
$reflectionClass = new ReflectionClass($className);
if ($this->hasStaticAnnotation($methodName, $reflectionClass)) {
return true;
}
// probably magic method → we don't know
if (! method_exists($className, $methodName)) {
return false;
}
$methodReflection = $reflectionClass->getMethod($methodName);
return $methodReflection->isStatic();
}
private function hasStaticAnnotation(string $methodName, ReflectionClass $reflectionClass): bool
{
return (bool) Strings::match(
(string) $reflectionClass->getDocComment(),
'#@method\s*static\s*(.*?)\b' . $methodName . '\b#'
);
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Rector\NodeCollector\ValueObject;
final class ArrayCallable
{
/**
* @var string
*/
private $class;
/**
* @var string
*/
private $method;
public function __construct(string $class, string $method)
{
$this->class = $class;
$this->method = $method;
}
public function getClass(): string
{
return $this->class;
}
public function getMethod(): string
{
return $this->method;
}
}

View File

@ -77,36 +77,57 @@ final class PHPStanServicesFactory
}
}
/**
* @api
*/
public function createReflectionProvider(): ReflectionProvider
{
return $this->container->getByType(ReflectionProvider::class);
}
/**
* @api
*/
public function createNodeScopeResolver(): NodeScopeResolver
{
return $this->container->getByType(NodeScopeResolver::class);
}
/**
* @api
*/
public function createTypeSpecifier(): TypeSpecifier
{
return $this->container->getByType(TypeSpecifier::class);
}
/**
* @api
*/
public function createScopeFactory(): ScopeFactory
{
return $this->container->getByType(ScopeFactory::class);
}
/**
* @api
*/
public function createDynamicReturnTypeExtensionRegistryProvider(): DynamicReturnTypeExtensionRegistryProvider
{
return $this->container->getByType(DynamicReturnTypeExtensionRegistryProvider::class);
}
/**
* @api
*/
public function createOperatorTypeSpecifyingExtensionRegistryProvider(): OperatorTypeSpecifyingExtensionRegistryProvider
{
return $this->container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class);
}
/**
* @api
*/
public function createTypeNodeResolver(): TypeNodeResolver
{
return $this->container->getByType(TypeNodeResolver::class);

View File

@ -1,60 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Finder;
use Nette\Loaders\RobotLoader;
use Nette\Utils\Strings;
final class PHPStanTypeClassFinder
{
/**
* @return string[]
*/
public function find(): array
{
$robotLoader = new RobotLoader();
$robotLoader->addDirectory($this->getPhpstanPharSrcTypeDirectoryPath());
$robotLoader->setTempDirectory(sys_get_temp_dir() . '/_phpstan_types');
$robotLoader->acceptFiles = ['*Type.php'];
$robotLoader->rebuild();
$classLikesToFilePaths = $robotLoader->getIndexedClasses();
$classLikes = array_keys($classLikesToFilePaths);
return $this->filterClassesOnly($classLikes);
}
/**
* @see https://github.com/dg/nette-robot-loader/blob/593c0e40e511c0b0700610a6a3964a210219139f/tests/Loaders/RobotLoader.phar.phpt#L33
*/
private function getPhpstanPharSrcTypeDirectoryPath(): string
{
$phpstanPharRealpath = realpath(__DIR__ . '/../../../../vendor/phpstan/phpstan/phpstan.phar');
return 'phar://' . $phpstanPharRealpath . '/src/Type';
}
/**
* @param string[] $classLikes
* @return string[]
*/
private function filterClassesOnly(array $classLikes): array
{
$classes = [];
foreach ($classLikes as $classLike) {
if (! class_exists($classLike)) {
continue;
}
if (Strings::match($classLike, '#\\\\Accessory\\\\#')) {
continue;
}
$classes[] = $classLike;
}
return $classes;
}
}

View File

@ -42,7 +42,7 @@ final class AttributeKey
/**
* @var string
*/
public const CLASS_SHORT_NAME = 'class_short_name';
public const CLASS_SHORT_NAME = 'classShortName';
/**
* @todo split Class node, interface node and trait node, to be compatible with other SpecificNode|null, values
@ -65,6 +65,11 @@ final class AttributeKey
*/
public const METHOD_NODE = ClassMethod::class;
/**
* @var string
*/
public const FUNCTION_NODE = Function_::class;
/**
* Internal php-parser name.
* Do not change this even if you want!
@ -112,6 +117,9 @@ final class AttributeKey
public const FILE_INFO = SmartFileInfo::class;
/**
* Internal php-parser name.
* Do not change this even if you want!
*
* @var string
*/
public const START_TOKEN_POSITION = 'startTokenPos';
@ -125,12 +133,7 @@ final class AttributeKey
/**
* @var string
*/
public const IS_UNREACHABLE = 'is_unreachable';
/**
* @var string
*/
public const FUNCTION_NODE = Function_::class;
public const IS_UNREACHABLE = 'isUnreachable';
/**
* @var string
@ -140,5 +143,5 @@ final class AttributeKey
/**
* @var string
*/
public const METHOD_CALL_NODE_CALLER_NAME = 'method_call_variable_name';
public const METHOD_CALL_NODE_CALLER_NAME = 'methodCallVariableName';
}

View File

@ -7,7 +7,9 @@ namespace Rector\NodeTypeResolver\NodeTypeResolver;
use PhpParser\Builder\Property;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Nop;
use PhpParser\Node\Stmt\Trait_;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\MixedType;
@ -19,6 +21,7 @@ use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use ReflectionProperty;
@ -52,16 +55,23 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
*/
private $staticTypeMapper;
/**
* @var TraitNodeScopeCollector
*/
private $traitNodeScopeCollector;
public function __construct(
ParsedNodeCollector $parsedNodeCollector,
NodeNameResolver $nodeNameResolver,
BetterPhpDocParser $betterPhpDocParser,
StaticTypeMapper $staticTypeMapper
StaticTypeMapper $staticTypeMapper,
TraitNodeScopeCollector $traitNodeScopeCollector
) {
$this->parsedNodeCollector = $parsedNodeCollector;
$this->nodeNameResolver = $nodeNameResolver;
$this->betterPhpDocParser = $betterPhpDocParser;
$this->staticTypeMapper = $staticTypeMapper;
$this->traitNodeScopeCollector = $traitNodeScopeCollector;
}
/**
@ -87,58 +97,77 @@ final class PropertyFetchTypeResolver implements NodeTypeResolverInterface
{
// compensate 3rd party non-analysed property reflection
$vendorPropertyType = $this->getVendorPropertyFetchType($node);
if ($vendorPropertyType !== null) {
if (! $vendorPropertyType instanceof MixedType) {
return $vendorPropertyType;
}
/** @var Scope|null $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope === null) {
return new MixedType();
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
if ($classNode instanceof Trait_) {
/** @var string $traitName */
$traitName = $classNode->getAttribute(AttributeKey::CLASS_NAME);
/** @var Scope|null $scope */
$scope = $this->traitNodeScopeCollector->getScopeForTraitAndNode($traitName, $node);
}
}
if ($scope === null) {
$classNode = $node->getAttribute(AttributeKey::CLASS_NODE);
// fallback to class, since property fetches are not scoped by PHPStan
if ($classNode instanceof ClassLike) {
$scope = $classNode->getAttribute(AttributeKey::SCOPE);
}
if ($scope === null) {
return new MixedType();
}
}
return $scope->getType($node);
}
private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): ?Type
private function getVendorPropertyFetchType(PropertyFetch $propertyFetch): Type
{
$varObjectType = $this->nodeTypeResolver->resolve($propertyFetch->var);
if (! $varObjectType instanceof TypeWithClassName) {
return null;
return new MixedType();
}
$class = $this->parsedNodeCollector->findClass($varObjectType->getClassName());
if ($class !== null) {
return null;
return new MixedType();
}
// 3rd party code
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);
if ($propertyName === null) {
return null;
return new MixedType();
}
if (! property_exists($varObjectType->getClassName(), $propertyName)) {
return null;
return new MixedType();
}
// property is used
$propertyReflection = new ReflectionProperty($varObjectType->getClassName(), $propertyName);
if (! $propertyReflection->getDocComment()) {
return null;
return new MixedType();
}
$phpDocNode = $this->betterPhpDocParser->parseString((string) $propertyReflection->getDocComment());
$varTagValues = $phpDocNode->getVarTagValues();
if (! isset($varTagValues[0])) {
return null;
return new MixedType();
}
$typeNode = $varTagValues[0]->type;
if (! $typeNode instanceof TypeNode) {
return null;
return new MixedType();
}
return $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, new Nop());

View File

@ -50,9 +50,4 @@ final class PropertyTypeResolver implements NodeTypeResolverInterface
return $this->nodeTypeResolver->resolve($propertyFetchNode);
}
public function setNodeTypeResolver(NodeTypeResolver $nodeTypeResolver): void
{
$this->nodeTypeResolver = $nodeTypeResolver;
}
}

View File

@ -53,7 +53,7 @@ final class StaticTypeAnalyzer
return $this->isAlwaysTruableUnionType($type);
}
public function isNullable(Type $type): bool
private function isNullable(Type $type): bool
{
if (! $type instanceof UnionType) {
return false;

View File

@ -52,12 +52,7 @@ final class VendorLockResolver
return false;
}
return $this->isParamChangeVendorLockedIn($node, $paramPosition);
}
public function isParamChangeVendorLockedIn(ClassMethod $classMethod, int $paramPosition): bool
{
return $this->classMethodParamVendorLockResolver->isVendorLocked($classMethod, $paramPosition);
return $this->classMethodParamVendorLockResolver->isVendorLocked($node, $paramPosition);
}
public function isReturnChangeVendorLockedIn(ClassMethod $classMethod): bool

View File

@ -5,7 +5,7 @@ includes:
- 'vendor/phpstan/phpstan/conf/bleedingEdge.neon'
parameters:
# checkGenericClassInNonGenericObjectType: false
checkGenericClassInNonGenericObjectType: false
level: max
# to allow installing with various phsptan versions without reporting old errors here
@ -246,4 +246,4 @@ parameters:
- '#Access to an undefined property PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode\:\:\$description#'
- '#Method Rector\\Php80\\Rector\\NotIdentical\\StrContainsRector\:\:matchNotIdenticalToFalse\(\) should return PhpParser\\Node\\Expr\\FuncCall\|null but returns PhpParser\\Node\\Expr#'
- '#Method Rector\\Php80\\Rector\\NotIdentical\\StrContainsRector\:\:matchPossibleFuncCallToFalseOrZero\(\) should return PhpParser\\Node\\Expr\\FuncCall\|null but returns PhpParser\\Node\\Expr#'
- '#Method Rector\\NodeCollector\\StaticAnalyzer\:\:hasStaticAnnotation\(\) has parameter \$reflectionClass with generic class ReflectionClass but does not specify its types\: T#'

View File

@ -5,12 +5,14 @@ parameters:
- 'dead-code'
- 'nette-utils-code-quality'
- 'solid'
- 'privatization'
paths:
- 'src'
- 'rules'
- 'packages'
- 'tests'
- 'utils'
exclude_paths:
- '/Fixture/'

View File

@ -37,23 +37,6 @@ final class DoctrineNodeFactory
$this->phpDocInfoFactory = $phpDocInfoFactory;
}
/**
* Creates:
* $this->repository = $entityManager->getRepository(\EntityClass::class);
*/
public function createRepositoryAssign(string $entityClass): Assign
{
$repositoryPropertyFetch = new PropertyFetch(new Variable('this'), new Identifier('repository'));
$entityClassReference = new ClassConstFetch(new FullyQualified($entityClass), 'class');
$getRepositoryMethodCall = new MethodCall(new Variable('entityManager'), 'getRepository', [
new Arg($entityClassReference),
]);
return new Assign($repositoryPropertyFetch, $getRepositoryMethodCall);
}
public function createRepositoryProperty(): Property
{
$repositoryPropertyBuilder = $this->builderFactory->property('repository');
@ -82,4 +65,21 @@ final class DoctrineNodeFactory
->addStmt($assign)
->getNode();
}
/**
* Creates:
* $this->repository = $entityManager->getRepository(\EntityClass::class);
*/
private function createRepositoryAssign(string $entityClass): Assign
{
$repositoryPropertyFetch = new PropertyFetch(new Variable('this'), new Identifier('repository'));
$entityClassReference = new ClassConstFetch(new FullyQualified($entityClass), 'class');
$getRepositoryMethodCall = new MethodCall(new Variable('entityManager'), 'getRepository', [
new Arg($entityClassReference),
]);
return new Assign($repositoryPropertyFetch, $getRepositoryMethodCall);
}
}

View File

@ -67,7 +67,7 @@ final class EventSubscriberClassFactory
return dirname($fileInfo->getRealPath()) . DIRECTORY_SEPARATOR . $eventSubscriberClassName . '.php';
}
public function createEventSubscriberClassName(Class_ $class): string
private function createEventSubscriberClassName(Class_ $class): string
{
$className = $class->getAttribute(AttributeKey::CLASS_SHORT_NAME);

View File

@ -12,6 +12,7 @@ use PhpParser\Node\Stmt\Trait_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeCollector\NodeFinder\MethodCallParsedNodesFinder;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
@ -19,6 +20,16 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
*/
final class RemoveUnusedPrivateMethodRector extends AbstractRector
{
/**
* @var MethodCallParsedNodesFinder
*/
private $methodCallParsedNodesFinder;
public function __construct(MethodCallParsedNodesFinder $methodCallParsedNodesFinder)
{
$this->methodCallParsedNodesFinder = $methodCallParsedNodesFinder;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Remove unused private method', [
@ -68,7 +79,7 @@ PHP
return null;
}
$classMethodCalls = $this->functionLikeParsedNodesFinder->findClassMethodCalls($node);
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($node);
if ($classMethodCalls !== []) {
return null;
}

View File

@ -110,7 +110,7 @@ PHP
/** @var ClassMethod $method */
foreach ($classMethodsToCheck as $method) {
if (! $this->methodHasNoStmtsLeft($method)) {
if (! $this->hasMethodSomeStmtsLeft($method)) {
continue;
}
@ -125,7 +125,7 @@ PHP
return $node;
}
protected function methodHasNoStmtsLeft(ClassMethod $classMethod): bool
private function hasMethodSomeStmtsLeft(ClassMethod $classMethod): bool
{
foreach ((array) $classMethod->stmts as $stmt) {
if (! $this->isNodeRemoved($stmt)) {

View File

@ -40,27 +40,6 @@ final class UnusedClassResolver
$this->parsedNodeCollector = $parsedNodeCollector;
}
/**
* @return string[]
*/
public function getUsedClassNames(): array
{
if (! PHPUnitEnvironment::isPHPUnitRun() && $this->cachedUsedClassNames !== []) {
return $this->cachedUsedClassNames;
}
$cachedUsedClassNames = array_merge(
$this->getParamNodesClassNames(),
$this->getNewNodesClassNames(),
$this->getStaticCallClassNames(),
$this->getClassConstantFetchNames()
);
$cachedUsedClassNames = $this->sortAndUniqueArray($cachedUsedClassNames);
return $this->cachedUsedClassNames = $cachedUsedClassNames;
}
public function isClassWithoutInterfaceAndNotController(Class_ $class): bool
{
if ($class->implements !== []) {
@ -82,6 +61,27 @@ final class UnusedClassResolver
return $this->nodeNameResolver->isNames($class, $this->getUsedClassNames());
}
/**
* @return string[]
*/
private function getUsedClassNames(): array
{
if (! PHPUnitEnvironment::isPHPUnitRun() && $this->cachedUsedClassNames !== []) {
return $this->cachedUsedClassNames;
}
$cachedUsedClassNames = array_merge(
$this->getParamNodesClassNames(),
$this->getNewNodesClassNames(),
$this->getStaticCallClassNames(),
$this->getClassConstantFetchNames()
);
$cachedUsedClassNames = $this->sortAndUniqueArray($cachedUsedClassNames);
return $this->cachedUsedClassNames = $cachedUsedClassNames;
}
/**
* @return string[]
*/

View File

@ -39,7 +39,7 @@ final class EntityIdNodeFactory
return $uuidProperty;
}
public function decoratePropertyWithIdAnnotations(Property $property): void
private function decoratePropertyWithIdAnnotations(Property $property): void
{
/** @var PhpDocInfo $phpDocInfo */
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);

View File

@ -63,7 +63,7 @@ final class EntityUuidNodeFactory
return new Expression($assign);
}
public function decoratePropertyWithUuidAnnotations(Property $property, bool $isNullable, bool $isId): void
private function decoratePropertyWithUuidAnnotations(Property $property, bool $isNullable, bool $isId): void
{
$this->clearVarAndOrmAnnotations($property);
$this->replaceIntSerializerTypeWithString($property);

View File

@ -77,16 +77,6 @@ final class DoctrineDocBlockResolver
return $doctrineRelationTagValueNode->getTargetEntity();
}
public function hasPropertyDoctrineIdTag(Property $property): bool
{
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return false;
}
return $phpDocInfo->hasByType(IdTagValueNode::class);
}
public function getDoctrineRelationTagValueNode(Property $property): ?DoctrineRelationTagValueNodeInterface
{
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
@ -122,6 +112,13 @@ final class DoctrineDocBlockResolver
return $this->isDoctrineEntityClass($classNode);
}
private function hasPropertyDoctrineIdTag(Property $property): bool
{
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
return $phpDocInfo ? $phpDocInfo->hasByType(IdTagValueNode::class) : false;
}
private function isDoctrineEntityClassNode(Class_ $class): bool
{
$phpDocInfo = $class->getAttribute(AttributeKey::PHP_DOC_INFO);
@ -152,10 +149,6 @@ final class DoctrineDocBlockResolver
// dummy check of 3rd party code without running it
$docCommentContent = (string) $reflectionClass->getDocComment();
if (Strings::contains($docCommentContent, '@ORM\Entity')) {
return true;
}
return Strings::contains($docCommentContent, '@ORM\Embeddable');
return (bool) Strings::match($docCommentContent, '#@ORM\\\\(Entity|Embeddable)#');
}
}

View File

@ -62,46 +62,50 @@ use Nette\Application\UI\Control;
class SomeForm extends Control
{
public function createComponentForm()
{
$form = new Form();
$form->addText('name', 'Your name');
public function createComponentForm()
{
$form = new Form();
$form->addText('name', 'Your name');
$form->onSuccess[] = [$this, 'processForm'];
}
$form->onSuccess[] = [$this, 'processForm'];
}
public function processForm(Form $form)
{
public function processForm(Form $form)
{
// process me
}
}
}
PHP
,
<<<'PHP'
class SomeFormController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
/**
* @Route(...)
*/
public function actionSomeForm(\Symfony\Component\HttpFoundation\Request $request): \Symfony\Component\HttpFoundation\Response
{
$form = $this->createForm(SomeFormType::class);
$form->handleRequest($request);
/**
* @Route(...)
*/
public function actionSomeForm(\Symfony\Component\HttpFoundation\Request $request): \Symfony\Component\HttpFoundation\Response
{
$form = $this->createForm(SomeFormType::class);
$form->handleRequest($request);
if ($form->isSuccess() && $form->isValid()) {
// process me
}
}
if ($form->isSuccess() && $form->isValid()) {
// process me
}
}
}
PHP
,
<<<'PHP'
class SomeFormType extends \Symfony\Component\Form\AbstractType
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class SomeFormType extends AbstractType
{
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $formBuilder, array $options)
public function buildForm(FormBuilderInterface $formBuilder, array $options)
{
$formBuilder->add('name', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [
'label' => 'Your name'
$formBuilder->add('name', TextType::class, [
'label' => 'Your name'
]);
}
}

View File

@ -132,7 +132,7 @@ final class RouteInfoFactory
}
if (method_exists($presenterClass, 'run')) {
return new RouteInfo($presenterClass, 'run', $routePath, null, $methods);
return new RouteInfo($presenterClass, 'run', $routePath, $methods);
}
}
@ -177,6 +177,6 @@ final class RouteInfoFactory
return null;
}
return new RouteInfo($controllerClass, $methodName, $routePath, null, []);
return new RouteInfo($controllerClass, $methodName, $routePath, []);
}
}

View File

@ -38,6 +38,7 @@ final class SymfonyFormAbstractTypeFactory
}
/**
* @api
* @param MethodCall[] $methodCalls
*/
public function createFromNetteFormMethodCalls(array $methodCalls): Class_

View File

@ -21,11 +21,6 @@ final class RouteInfo
*/
private $path;
/**
* @var string|null
*/
private $name;
/**
* @var string[]
*/
@ -34,17 +29,11 @@ final class RouteInfo
/**
* @param string[] $httpMethods
*/
public function __construct(
string $class,
string $method,
string $path,
?string $name = null,
array $httpMethods = []
) {
public function __construct(string $class, string $method, string $path, array $httpMethods = [])
{
$this->class = $class;
$this->method = $method;
$this->path = $path;
$this->name = $name;
$this->httpMethods = $httpMethods;
}
@ -63,11 +52,6 @@ final class RouteInfo
return $this->path;
}
public function getName(): ?string
{
return $this->name;
}
/**
* @return string[]
*/

View File

@ -68,7 +68,7 @@ final class EregToPcreTransformer
}
// converts the ERE $s into the PCRE $r. triggers error on any invalid input.
public function ere2pcre(string $content, bool $ignorecase): string
private function ere2pcre(string $content, bool $ignorecase): string
{
if ($ignorecase) {
if (isset($this->icache[$content])) {

View File

@ -11,6 +11,7 @@ use PHPUnit\Framework\TestCase;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeCollector\StaticAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
@ -19,6 +20,16 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
*/
final class ThisCallOnStaticMethodToStaticCallRector extends AbstractRector
{
/**
* @var StaticAnalyzer
*/
private $staticAnalyzer;
public function __construct(StaticAnalyzer $staticAnalyzer)
{
$this->staticAnalyzer = $staticAnalyzer;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Changes $this->call() to static method to static call', [
@ -89,7 +100,7 @@ PHP
return null;
}
$isStaticMethod = $this->functionLikeParsedNodesFinder->isStaticMethod($methodName, $className);
$isStaticMethod = $this->staticAnalyzer->isStaticMethod($methodName, $className);
if (! $isStaticMethod) {
return null;
}

View File

@ -14,6 +14,7 @@ use Rector\Core\PhpParser\Node\Manipulator\ClassMethodManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeCollector\StaticAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use ReflectionClass;
@ -31,9 +32,15 @@ final class StaticCallOnNonStaticToInstanceCallRector extends AbstractRector
*/
private $classMethodManipulator;
public function __construct(ClassMethodManipulator $classMethodManipulator)
/**
* @var StaticAnalyzer
*/
private $staticAnalyzer;
public function __construct(ClassMethodManipulator $classMethodManipulator, StaticAnalyzer $staticAnalyzer)
{
$this->classMethodManipulator = $classMethodManipulator;
$this->staticAnalyzer = $staticAnalyzer;
}
public function getDefinition(): RectorDefinition
@ -97,7 +104,7 @@ PHP
return null;
}
$isStaticMethod = $this->functionLikeParsedNodesFinder->isStaticMethod($methodName, $className);
$isStaticMethod = $this->staticAnalyzer->isStaticMethod($methodName, $className);
if ($isStaticMethod) {
return null;
}

View File

@ -16,8 +16,6 @@ use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareParamTagValueNode;
@ -206,20 +204,6 @@ PHP
return 'provideDataFor' . ucfirst($methodName);
}
private function resolveUniqueArrayStaticType(Array_ $array): Type
{
$isNestedArray = $this->isNestedArray($array);
$uniqueArrayStaticType = $this->resolveUniqueArrayStaticTypes($array);
if ($isNestedArray && $uniqueArrayStaticType instanceof ArrayType) {
// unwrap one level up
return $uniqueArrayStaticType->getItemType();
}
return $uniqueArrayStaticType;
}
/**
* @return ParamAndArgValueObject[]
*/
@ -296,21 +280,6 @@ PHP
return false;
}
private function resolveUniqueArrayStaticTypes(Array_ $array): Type
{
$itemStaticTypes = [];
foreach ($array->items as $arrayItem) {
$arrayItemStaticType = $this->getStaticType($arrayItem->value);
if ($arrayItemStaticType instanceof MixedType) {
continue;
}
$itemStaticTypes[] = new ArrayType(new MixedType(), $arrayItemStaticType);
}
return $this->typeFactory->createMixedPassedOrUnionType($itemStaticTypes);
}
private function resolveItemStaticType(Array_ $array, bool $isNestedArray): Type
{
$staticTypes = [];
@ -380,8 +349,7 @@ PHP
$this->dataProviderClassMethodRecipes[] = new DataProviderClassMethodRecipe(
$dataProviderMethodName,
$methodCall->args,
$this->resolveUniqueArrayStaticType($firstArgumentValue)
$methodCall->args
);
$methodCall->args = [];

View File

@ -100,7 +100,7 @@ final class AssertComparisonToSpecificMethodRector extends AbstractPHPUnitRector
/**
* @param MethodCall|StaticCall $node
*/
public function changeOrderArguments(Node $node): void
private function changeArgumentsOrder(Node $node): void
{
$oldArguments = $node->args;
@ -137,7 +137,7 @@ final class AssertComparisonToSpecificMethodRector extends AbstractPHPUnitRector
'assertFalse' => $falseMethodName,
]);
$this->changeOrderArguments($node);
$this->changeArgumentsOrder($node);
return $node;
}

View File

@ -79,7 +79,7 @@ final class AssertFalseStrposToContainsRector extends AbstractPHPUnitRector
}
$this->identifierManipulator->renameNodeWithMap($node, self::RENAME_METHODS_MAP);
$this->changeOrderArguments($node);
$this->changeArgumentsOrder($node);
return $node;
}
@ -87,7 +87,7 @@ final class AssertFalseStrposToContainsRector extends AbstractPHPUnitRector
/**
* @param MethodCall|StaticCall $node
*/
public function changeOrderArguments(Node $node): void
private function changeArgumentsOrder(Node $node): void
{
$oldArguments = $node->args;

View File

@ -76,7 +76,7 @@ final class AssertInstanceOfComparisonRector extends AbstractPHPUnitRector
return null;
}
$this->identifierManipulator->renameNodeWithMap($node, self::RENAME_METHODS_MAP);
$this->changeOrderArguments($node);
$this->changeArgumentsOrder($node);
return $node;
}
@ -84,7 +84,7 @@ final class AssertInstanceOfComparisonRector extends AbstractPHPUnitRector
/**
* @param MethodCall|StaticCall $node
*/
public function changeOrderArguments(Node $node): void
private function changeArgumentsOrder(Node $node): void
{
$oldArguments = $node->args;
/** @var Instanceof_ $comparison */

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Rector\PHPUnit\ValueObject;
use PhpParser\Node\Arg;
use PHPStan\Type\Type;
final class DataProviderClassMethodRecipe
{
@ -19,19 +18,13 @@ final class DataProviderClassMethodRecipe
*/
private $args = [];
/**
* @var Type|null
*/
private $type;
/**
* @param Arg[] $args
*/
public function __construct(string $methodName, array $args, ?Type $type)
public function __construct(string $methodName, array $args)
{
$this->methodName = $methodName;
$this->args = $args;
$this->type = $type;
}
public function getMethodName(): string
@ -46,9 +39,4 @@ final class DataProviderClassMethodRecipe
{
return $this->args;
}
public function getType(): ?Type
{
return $this->type;
}
}

View File

@ -70,7 +70,7 @@ PHP
*/
public function refactor(Node $node): ?Node
{
if ($this->lastStatementBreaksFlow($node)) {
if ($this->doesLastStatementBreakFlow($node)) {
return null;
}
@ -99,7 +99,7 @@ PHP
return null;
}
protected function lastStatementBreaksFlow(Node $node): bool
private function doesLastStatementBreakFlow(Node $node): bool
{
$lastStmt = end($node->stmts);
return ! ($lastStmt instanceof Return_

View File

@ -222,7 +222,7 @@ final class ServiceMapProvider
);
} else {
unset($data['name']);
$tagValueObjects[$key] = new Tag($name, $data);
$tagValueObjects[$key] = new Tag($name);
}
}

View File

@ -28,11 +28,6 @@ final class ServiceMap
return isset($this->services[$id]);
}
public function getService(string $id): ?ServiceDefinition
{
return $this->services[$id] ?? null;
}
public function getServiceType(string $id): ?Type
{
$serviceDefinition = $this->getService($id);
@ -75,4 +70,9 @@ final class ServiceMap
return $servicesWithTag;
}
private function getService(string $id): ?ServiceDefinition
{
return $this->services[$id] ?? null;
}
}

View File

@ -13,24 +13,13 @@ final class Tag implements TagInterface
*/
private $name;
/**
* @var array
*/
private $data = [];
public function __construct(string $name, array $data)
public function __construct(string $name)
{
$this->name = $name;
$this->data = $data;
}
public function getName(): string
{
return $this->name;
}
public function getData(): array
{
return $this->data;
}
}

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Rector\TypeDeclaration\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
@ -14,6 +13,8 @@ use PHPStan\Type\Type;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeCollector\NodeFinder\MethodCallParsedNodesFinder;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
/**
@ -28,9 +29,15 @@ final class AddMethodCallBasedParamTypeRector extends AbstractRector
*/
private $typeFactory;
public function __construct(TypeFactory $typeFactory)
/**
* @var MethodCallParsedNodesFinder
*/
private $methodCallParsedNodesFinder;
public function __construct(TypeFactory $typeFactory, MethodCallParsedNodesFinder $methodCallParsedNodesFinder)
{
$this->typeFactory = $typeFactory;
$this->methodCallParsedNodesFinder = $methodCallParsedNodesFinder;
}
public function getDefinition(): RectorDefinition
@ -91,8 +98,7 @@ PHP
*/
public function refactor(Node $node): ?Node
{
$classMethodCalls = $this->functionLikeParsedNodesFinder->findClassMethodCalls($node);
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($node);
$classParameterTypes = $this->getCallTypesByPosition($classMethodCalls);
foreach ($classParameterTypes as $position => $argumentStaticType) {
@ -110,7 +116,7 @@ PHP
}
/**
* @param MethodCall[]|StaticCall[]|Array_[] $classMethodCalls
* @param MethodCall[]|StaticCall[]|ArrayCallable[] $classMethodCalls
* @return Type[]
*/
private function getCallTypesByPosition(array $classMethodCalls): array

View File

@ -79,6 +79,9 @@ final class Configuration
$this->setOnlyRector($onlyRector);
}
/**
* @api
*/
public function setFirstResolverConfig(?string $firstResolvedConfig): void
{
$this->configFilePath = $firstResolvedConfig;

View File

@ -9,4 +9,9 @@ interface CodeSampleInterface
public function getCodeBefore(): string;
public function getCodeAfter(): string;
/**
* Content of newly created file
*/
public function getExtraFileContent(): ?string;
}

View File

@ -44,6 +44,7 @@ final class RectorContainerFactory
}
/**
* @api
* @param string[] $configFiles
*/
public function createFromConfigs(array $configFiles): ContainerInterface

View File

@ -68,50 +68,6 @@ final class CallReflectionResolver
return $this->resolveMethodCall($node);
}
/**
* @return FunctionReflection|MethodReflection|null
*/
public function resolveFunctionCall(FuncCall $funcCall)
{
/** @var Scope|null $scope */
$scope = $funcCall->getAttribute(AttributeKey::SCOPE);
if ($funcCall->name instanceof Name) {
try {
return $this->reflectionProvider->getFunction($funcCall->name, $scope);
} catch (FunctionNotFoundException $functionNotFoundException) {
return null;
}
}
if ($scope === null) {
return null;
}
return $this->typeToCallReflectionResolverRegistry->resolve($scope->getType($funcCall->name), $scope);
}
/**
* @param MethodCall|StaticCall $node
*/
public function resolveMethodCall(Node $node): ?MethodReflection
{
/** @var Scope|null $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope === null) {
return null;
}
$classType = $this->nodeTypeResolver->resolve($node instanceof MethodCall ? $node->var : $node->class);
$methodName = $this->nodeNameResolver->getName($node->name);
if ($methodName === null || ! $classType->hasMethod($methodName)->yes()) {
return null;
}
return $classType->getMethod($methodName, $scope);
}
/**
* @param FunctionReflection|MethodReflection|null $reflection
* @param FuncCall|MethodCall|StaticCall $node
@ -141,4 +97,48 @@ final class CallReflectionResolver
return $parametersAcceptor;
}
/**
* @return FunctionReflection|MethodReflection|null
*/
private function resolveFunctionCall(FuncCall $funcCall)
{
/** @var Scope|null $scope */
$scope = $funcCall->getAttribute(AttributeKey::SCOPE);
if ($funcCall->name instanceof Name) {
try {
return $this->reflectionProvider->getFunction($funcCall->name, $scope);
} catch (FunctionNotFoundException $functionNotFoundException) {
return null;
}
}
if ($scope === null) {
return null;
}
return $this->typeToCallReflectionResolverRegistry->resolve($scope->getType($funcCall->name), $scope);
}
/**
* @param MethodCall|StaticCall $node
*/
private function resolveMethodCall(Node $node): ?MethodReflection
{
/** @var Scope|null $scope */
$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope === null) {
return null;
}
$classType = $this->nodeTypeResolver->resolve($node instanceof MethodCall ? $node->var : $node->class);
$methodName = $this->nodeNameResolver->getName($node->name);
if ($methodName === null || ! $classType->hasMethod($methodName)->yes()) {
return null;
}
return $classType->getMethod($methodName, $scope);
}
}

View File

@ -87,36 +87,20 @@ final class BetterNodeDumper extends NodeDumper
return $r;
}
/**
* @param mixed $attribute
*/
protected function dumpAttributeValue($attribute): string
{
if ($attribute === null) {
return 'null';
} elseif ($attribute === false) {
return 'false';
} elseif ($attribute === true) {
return 'true';
} elseif (is_scalar($attribute)) {
return (string) $attribute;
}
return str_replace("\n", "\n ", $this->dumpRecursive($attribute));
}
/**
* @return mixed[]
*/
protected function getFilteredAttributes(Node $node) : array
private function getFilteredAttributes(Node $node) : array
{
$attributes = $node->getAttributes();
if ($this->filterAttributes !== []) {
$attributes = array_intersect_key($attributes, array_flip($this->filterAttributes));
}
return $attributes;
}
protected function dumpSubNodes(Node $node): string
private function dumpSubNodes(Node $node): string
{
$r = '';
foreach ($node->getSubNodeNames() as $key) {
@ -153,15 +137,15 @@ final class BetterNodeDumper extends NodeDumper
}
protected function dumpNode(Node $node): string
private function dumpNode(Node $node): string
{
$spl_object_hash = spl_object_hash($node);
$splObjectHash = spl_object_hash($node);
if (isset($this->printedNodes[$spl_object_hash])) {
return $node->getType() . ' #' . $this->printedNodes[$spl_object_hash];
if (isset($this->printedNodes[$splObjectHash])) {
return $node->getType() . ' #' . $this->printedNodes[$splObjectHash];
}
$this->printedNodes[$spl_object_hash] = $this->nodeIds++;
$r = $node->getType() . ' #' . $this->printedNodes[$spl_object_hash];
$this->printedNodes[$splObjectHash] = $this->nodeIds++;
$r = $node->getType() . ' #' . $this->printedNodes[$splObjectHash];
if ($this->dumpPositions ) {
$r .= $this->dumpPosition($node);

View File

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Rector\Core\PhpParser\Node\Manipulator;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\AssignOp;
@ -38,76 +37,17 @@ final class AssignManipulator
*/
private $nodeNameResolver;
/**
* @var PropertyFetchManipulator
*/
private $propertyFetchManipulator;
/**
* @var BetterStandardPrinter
*/
private $betterStandardPrinter;
public function __construct(
NodeNameResolver $nodeNameResolver,
PropertyFetchManipulator $propertyFetchManipulator,
BetterStandardPrinter $betterStandardPrinter
) {
public function __construct(NodeNameResolver $nodeNameResolver, BetterStandardPrinter $betterStandardPrinter)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->propertyFetchManipulator = $propertyFetchManipulator;
$this->betterStandardPrinter = $betterStandardPrinter;
}
/**
* Checks:
* $this->x = y;
* $this->x[] = y;
*/
public function isLocalPropertyAssign(Node $node): bool
{
if (! $node instanceof Assign) {
return false;
}
return (bool) $this->propertyFetchManipulator->matchPropertyFetch($node->var);
}
/**
* Is: "$this->value = <$value>"
*
* @param string[] $propertyNames
*/
public function isLocalPropertyAssignWithPropertyNames(Node $node, array $propertyNames): bool
{
if (! $this->isLocalPropertyAssign($node)) {
return false;
}
/** @var Assign $node */
$propertyFetch = $this->propertyFetchManipulator->matchPropertyFetch($node->var);
if ($propertyFetch === null) {
return false;
}
return $this->nodeNameResolver->isNames($propertyFetch, $propertyNames);
}
/**
* Covers:
* - $this->propertyName = <$expr>;
* - self::$propertyName = <$expr>;
* - $this->propertyName[] = <$expr>;
* - self::$propertyName[] = <$expr>;
*/
public function matchPropertyAssignExpr(Assign $assign, string $propertyName): ?Expr
{
if (! $this->isLocalPropertyAssignWithPropertyNames($assign, [$propertyName])) {
return null;
}
return $assign->expr;
}
/**
* Matches:
* each() = [1, 2];

View File

@ -69,7 +69,6 @@ final class ChildAndParentClassManipulator
if ($parentClassName !== '' && method_exists($parentClassName, '__construct')) {
$parentConstructCallNode = $this->nodeFactory->createParentConstructWithParams([]);
$classMethod->stmts[] = new Expression($parentConstructCallNode);
return;
}
}

View File

@ -93,9 +93,11 @@ final class ClassDependencyManipulator
$constructorMethod = $this->nodeFactory->createPublicMethod(self::CONSTRUCTOR);
$this->classMethodAssignManipulator->addParameterAndAssignToMethod($constructorMethod, $name, $type, $assign);
$this->childAndParentClassManipulator->completeParentConstructor($classNode, $constructorMethod);
$this->classInsertManipulator->addAsFirstMethod($classNode, $constructorMethod);
$this->childAndParentClassManipulator->completeChildConstructors($classNode, $constructorMethod);
}

View File

@ -50,25 +50,6 @@ final class ClassInsertManipulator
$class->stmts[] = $stmt;
}
/**
* @param string[] ...$types
*/
public function addStatementToClassBeforeTypes(Class_ $class, Stmt $stmt, string ...$types): void
{
foreach ($types as $type) {
foreach ($class->stmts as $key => $classStmt) {
if (! $classStmt instanceof $type) {
continue;
}
$class->stmts = $this->insertBefore($class->stmts, $stmt, $key);
return;
}
}
$class->stmts[] = $stmt;
}
/**
* @param Stmt[] $nodes
* @return Stmt[]
@ -106,6 +87,25 @@ final class ClassInsertManipulator
$this->addStatementToClassBeforeTypes($class, $trait, TraitUse::class, Property::class);
}
/**
* @param string[] ...$types
*/
private function addStatementToClassBeforeTypes(Class_ $class, Stmt $stmt, string ...$types): void
{
foreach ($types as $type) {
foreach ($class->stmts as $key => $classStmt) {
if (! $classStmt instanceof $type) {
continue;
}
$class->stmts = $this->insertBefore($class->stmts, $stmt, $key);
return;
}
}
$class->stmts[] = $stmt;
}
private function tryInsertBeforeFirstMethod(Class_ $classNode, Stmt $stmt): bool
{
foreach ($classNode->stmts as $key => $classStmt) {

View File

@ -75,15 +75,6 @@ final class ClassMethodAssignManipulator
return $this->variableManipulator->filterOutReadOnlyVariables($readOnlyVariableAssigns, $classMethod);
}
public function isExplicitlyReferenced(Node $node): bool
{
if ($node instanceof Arg || $node instanceof ClosureUse || $node instanceof Param) {
return $node->byRef;
}
return false;
}
public function addParameterAndAssignToMethod(
ClassMethod $classMethod,
string $name,
@ -98,6 +89,15 @@ final class ClassMethodAssignManipulator
$classMethod->stmts[] = new Expression($assign);
}
private function isExplicitlyReferenced(Node $node): bool
{
if ($node instanceof Arg || $node instanceof ClosureUse || $node instanceof Param) {
return $node->byRef;
}
return false;
}
/**
* @param Assign[] $variableAssigns
* @return Assign[]
@ -118,6 +118,11 @@ final class ClassMethodAssignManipulator
}
foreach ($node->var->items as $arrayItem) {
// empty item
if ($arrayItem === null) {
continue;
}
if (! $arrayItem->value instanceof Variable) {
continue;
}

View File

@ -174,17 +174,6 @@ final class ClassMethodManipulator
return $paramName;
}
public function removeParameter(Param $param, ClassMethod $classMethod): void
{
foreach ($classMethod->params as $key => $constructorParam) {
if (! $this->nodeNameResolver->areNamesEqual($constructorParam, $param)) {
continue;
}
unset($classMethod->params[$key]);
}
}
public function removeUnusedParameters(ClassMethod $classMethod): void
{
foreach ($classMethod->getParams() as $param) {
@ -207,6 +196,17 @@ final class ClassMethodManipulator
return null;
}
private function removeParameter(Param $param, ClassMethod $classMethod): void
{
foreach ($classMethod->params as $key => $constructorParam) {
if (! $this->nodeNameResolver->areNamesEqual($constructorParam, $param)) {
continue;
}
unset($classMethod->params[$key]);
}
}
private function isMethodInParent(string $class, string $method): bool
{
foreach (class_parents($class) as $parentClass) {

View File

@ -127,24 +127,6 @@ final class IfManipulator
return null;
}
public function isIfWithOnlyStmtIf(If_ $if): bool
{
if (! $this->isIfWithoutElseAndElseIfs($if)) {
return false;
}
return $this->hasOnlyStmtOfType($if, If_::class);
}
public function hasOnlyStmtOfType(If_ $if, string $desiredType): bool
{
if (count($if->stmts) !== 1) {
return false;
}
return is_a($if->stmts[0], $desiredType);
}
/**
* @return If_[]
*/
@ -173,15 +155,6 @@ final class IfManipulator
return $ifs;
}
public function isIfWithElse(If_ $if): bool
{
if ($if->else === null) {
return false;
}
return ! (bool) $if->elseifs;
}
public function isIfAndElseWithSameVariableAssignAsLastStmts(If_ $if, Expr $desiredExpr): bool
{
if (! $this->isIfWithElse($if)) {
@ -289,6 +262,33 @@ final class IfManipulator
return $this->hasOnlyStmtOfType($node, Foreach_::class);
}
private function isIfWithOnlyStmtIf(If_ $if): bool
{
if (! $this->isIfWithoutElseAndElseIfs($if)) {
return false;
}
return $this->hasOnlyStmtOfType($if, If_::class);
}
private function hasOnlyStmtOfType(If_ $if, string $desiredType): bool
{
if (count($if->stmts) !== 1) {
return false;
}
return is_a($if->stmts[0], $desiredType);
}
private function isIfWithElse(If_ $if): bool
{
if ($if->else === null) {
return false;
}
return ! (bool) $if->elseifs;
}
private function matchComparedAndReturnedNode(NotIdentical $notIdentical, Return_ $returnNode): ?Expr
{
if ($this->betterStandardPrinter->areNodesEqual(

View File

@ -60,7 +60,7 @@ final class PropertyFetchAssignManipulator
* Matches:
* "$this->someValue = $<variableName>;"
*/
public function isVariableAssignToThisPropertyFetch(Node $node, string $variableName): bool
private function isVariableAssignToThisPropertyFetch(Node $node, string $variableName): bool
{
if (! $node instanceof Assign) {
return false;

View File

@ -5,10 +5,8 @@ declare(strict_types=1);
namespace Rector\Core\PhpParser\Node\Manipulator;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ReflectionProvider;
@ -142,26 +140,6 @@ final class PropertyFetchManipulator
return $this->nodeNameResolver->isName($node->var, 'this');
}
/**
* @return PropertyFetch|StaticPropertyFetch|null
*/
public function matchPropertyFetch(Node $node): ?Node
{
if ($node instanceof PropertyFetch) {
return $node;
}
if ($node instanceof StaticPropertyFetch) {
return $node;
}
if ($node instanceof ArrayDimFetch) {
return $this->matchPropertyFetch($node->var);
}
return null;
}
/**
* Matches:
* "$this->someValue = $<variableName>;"

View File

@ -6,7 +6,6 @@ namespace Rector\Core\Rector\AbstractRector;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
@ -24,6 +23,8 @@ use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\DeadCode\NodeManipulator\LivingCodeManipulator;
use Rector\NodeCollector\NodeCollector\ParsedNodeCollector;
use Rector\NodeCollector\NodeFinder\FunctionLikeParsedNodesFinder;
use Rector\NodeCollector\NodeFinder\MethodCallParsedNodesFinder;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
@ -53,6 +54,11 @@ trait ComplexRemovalTrait
*/
private $propertyManipulator;
/**
* @var MethodCallParsedNodesFinder
*/
private $methodCallParsedNodesFinder;
/**
* @required
*/
@ -60,12 +66,14 @@ trait ComplexRemovalTrait
PropertyManipulator $propertyManipulator,
ParsedNodeCollector $parsedNodeCollector,
LivingCodeManipulator $livingCodeManipulator,
BetterStandardPrinter $betterStandardPrinter
BetterStandardPrinter $betterStandardPrinter,
MethodCallParsedNodesFinder $methodCallParsedNodesFinder
): void {
$this->parsedNodeCollector = $parsedNodeCollector;
$this->propertyManipulator = $propertyManipulator;
$this->livingCodeManipulator = $livingCodeManipulator;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->methodCallParsedNodesFinder = $methodCallParsedNodesFinder;
}
abstract protected function removeNode(Node $node): void;
@ -74,9 +82,9 @@ trait ComplexRemovalTrait
{
$this->removeNode($classMethod);
$classMethodCalls = $this->functionLikeParsedNodesFinder->findClassMethodCalls($classMethod);
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($classMethod);
foreach ($classMethodCalls as $classMethodCall) {
if ($classMethodCall instanceof Array_) {
if ($classMethodCall instanceof ArrayCallable) {
continue;
}

View File

@ -40,7 +40,7 @@ final class CodeSample implements CodeSampleInterface
return $this->codeAfter;
}
public function getExtraFileAfter(): ?string
public function getExtraFileContent(): ?string
{
return $this->extraFileContent;
}

View File

@ -23,14 +23,24 @@ final class ConfiguredCodeSample implements CodeSampleInterface
*/
private $configuration = [];
/**
* @var string|null
*/
private $extraFileContent;
/**
* @param mixed[] $configuration
*/
public function __construct(string $codeBefore, string $codeAfter, array $configuration)
{
public function __construct(
string $codeBefore,
string $codeAfter,
array $configuration,
?string $extraFileContent = null
) {
$this->codeBefore = $codeBefore;
$this->codeAfter = $codeAfter;
$this->configuration = $configuration;
$this->extraFileContent = $extraFileContent;
}
public function getCodeBefore(): string
@ -50,4 +60,9 @@ final class ConfiguredCodeSample implements CodeSampleInterface
{
return $this->configuration;
}
public function getExtraFileContent(): ?string
{
return $this->extraFileContent;
}
}

View File

@ -55,7 +55,7 @@ final class FunctionReflectionHelper
return $this->expandAnnotatedClasses($reflectionFunction, $annotatedThrownClasses);
}
public function expandAnnotatedClasses(ReflectionFunction $reflectionFunction, array $classNames): array
private function expandAnnotatedClasses(ReflectionFunction $reflectionFunction, array $classNames): array
{
$functionNode = $this->functionParser->parseFunction($reflectionFunction);
if (! $functionNode instanceof Namespace_) {

View File

@ -172,7 +172,7 @@ final class ScannedErrorToRectorResolver
throw new NotImplementedException();
}
$arguments[] = new Argument($match['name'], $position, $match['type'] ?? '');
$arguments[] = new Argument($position, $match['type'] ?? '');
}
return $arguments;

View File

@ -11,20 +11,14 @@ final class Argument
*/
private $position;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $type;
public function __construct(string $name, int $position, string $type = '')
public function __construct(int $position, string $type = '')
{
$this->position = $position;
$this->name = $name;
$this->type = $type;
}
@ -33,11 +27,6 @@ final class Argument
return $this->position;
}
public function getName(): string
{
return $this->name;
}
public function getType(): string
{
return $this->type;

View File

@ -1,73 +0,0 @@
<?php
declare(strict_types=1);
use Nette\Utils\FileSystem;
use Nette\Utils\Json;
use Nette\Utils\Strings;
require __DIR__ . '/../../../vendor/autoload.php';
$composerJson = __DIR__ . '/../../../composer.json';
$composerContent = FileSystem::read($composerJson);
$jsonContent = Json::decode($composerContent, Json::FORCE_ARRAY);
// 1. unset primary here
unset($jsonContent['autoload']['psr-4']['Rector\\Core\\']);
unset($jsonContent['autoload-dev']['psr-4']['Rector\\Core\\Tests\\']);
// 2. sort by namespaces
ksort($jsonContent['autoload']['psr-4']);
ksort($jsonContent['autoload-dev']['psr-4']);
// 3. make core first
$jsonContent['autoload']['psr-4'] = array_merge(['Rector\\Core\\' => 'src'], $jsonContent['autoload']['psr-4']);
$jsonContent['autoload-dev']['psr-4'] = array_merge(
['Rector\\Core\\Tests\\' => 'tests'],
$jsonContent['autoload-dev']['psr-4']
);
$newComposerJsonContent = Json::encode($jsonContent, Json::PRETTY) . PHP_EOL;
$newComposerJsonContent = inlineSections($newComposerJsonContent, ['keywords', 'bin']);
$newComposerJsonContent = inlineAuthorSection($newComposerJsonContent);
FileSystem::write($composerJson, $newComposerJsonContent);
echo 'DONE';
// used from: https://github.com/Symplify/Symplify/blob/64e1e07c87b1ec5551df07482d68c5085e76824a/packages/MonorepoBuilder/src/FileSystem/JsonFileManager.php#L70
function inlineSections(string $jsonContent, array $inlineSections): string
{
foreach ($inlineSections as $inlineSection) {
$pattern = '#("' . preg_quote($inlineSection, '#') . '": )\[(.*?)\](,)#ms';
$jsonContent = Strings::replace($jsonContent, $pattern, function (array $match): string {
$inlined = Strings::replace($match[2], '#\s+#', ' ');
$inlined = trim($inlined);
$inlined = '[' . $inlined . ']';
return $match[1] . $inlined . $match[3];
});
}
return $jsonContent;
}
function inlineAuthorSection(string $jsonContent): string
{
$authorsPattern = '#("authors":\s+\[)(?<authors>.*?)(\])#s';
$match = Strings::match($jsonContent, $authorsPattern);
$authorsContent = $match['authors'];
$authorsContent = Strings::replace($authorsContent, '#,\s+#', ', ');
$authorsContent = Strings::replace($authorsContent, '#{\s+#', '{ ');
$authorsContent = Strings::replace($authorsContent, '#\s+}#', ' }');
$authorsContent = Strings::replace($authorsContent, '#}, {#', "},\n {");
$authorsContent = trim($authorsContent);
$authorsContent = ' ' . $authorsContent;
return Strings::replace($jsonContent, $authorsPattern, "$1\n" . $authorsContent . "\n $3");
}

View File

@ -15,6 +15,11 @@ use Symplify\PackageBuilder\Console\Command\CommandNaming;
final class DumpRectorsCommand extends AbstractCommand
{
/**
* @var string
*/
private const OUTPUT_FORMAT_OPTION = 'output-format';
/**
* @var DumpRectorsOutputFormatterInterface[]
*/
@ -41,7 +46,7 @@ final class DumpRectorsCommand extends AbstractCommand
$this->setName(CommandNaming::classToName(self::class));
$this->setDescription('[Docs] Dump overview of all Rectors');
$this->addOption(
'output-format',
self::OUTPUT_FORMAT_OPTION,
'o',
InputOption::VALUE_REQUIRED,
'Output format for Rectors [json, markdown]',
@ -59,7 +64,7 @@ final class DumpRectorsCommand extends AbstractCommand
$generalRectors = $this->rectorsFinder->findInDirectory(__DIR__ . '/../../../../src');
foreach ($this->dumpRectorsOutputFormatterInterfaces as $outputFormatter) {
if ($outputFormatter->getName() !== $input->getOption('output-format')) {
if ($outputFormatter->getName() !== $input->getOption(self::OUTPUT_FORMAT_OPTION)) {
continue;
}

View File

@ -211,6 +211,14 @@ final class MarkdownDumpRectorsOutputFormatter implements DumpRectorsOutputForma
);
$this->printCodeWrapped($diff, 'diff');
$extraFileContent = $codeSample->getExtraFileContent();
if ($extraFileContent !== null) {
$this->symfonyStyle->newLine();
$this->symfonyStyle->writeln('**New file**');
$this->symfonyStyle->newLine();
$this->printCodeWrapped($extraFileContent, 'php');
}
}
private function printCodeWrapped(string $content, string $format): void

View File

@ -8,6 +8,11 @@ use Nette\Loaders\RobotLoader;
final class NodeClassFinder
{
/**
* @var string[]
*/
private const EXCLUDED_CLASSES = ['PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode'];
/**
* @return string[]
*/
@ -40,7 +45,6 @@ final class NodeClassFinder
$classLikesToPaths = $robotLoader->getIndexedClasses();
$classLikes = array_keys($classLikesToPaths);
$excludedClasses = ['PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode'];
// keep only classes, skip interfaces
$classes = [];
@ -52,6 +56,6 @@ final class NodeClassFinder
$classes[] = $classLike;
}
return array_diff($classes, $excludedClasses);
return array_diff($classes, self::EXCLUDED_CLASSES);
}
}

View File

@ -31,9 +31,9 @@ use PHPStan\Type\Type;
abstract class AbstractResolvedNameReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
/**
* @var string[]
* @var class-string[]
*/
private $alwaysNamedTypes = [
private const ALWAYS_NAMED_TYPES = [
ClassMethod::class,
Trait_::class,
Interface_::class,
@ -57,7 +57,7 @@ abstract class AbstractResolvedNameReturnTypeExtension implements DynamicMethodR
return $returnType;
}
if (in_array($argumentValueType->getClassName(), $this->alwaysNamedTypes, true)) {
if (in_array($argumentValueType->getClassName(), self::ALWAYS_NAMED_TYPES, true)) {
return new StringType();
}

View File

@ -33,7 +33,7 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
/**
* @var string[]|string[][]
*/
private $argumentKeyToReturnType = [
private const ARGUMENT_KEY_TO_RETURN_TYPE = [
AttributeKey::class . '::FILE_INFO' => SmartFileInfo::class,
AttributeKey::class . '::RESOLVED_NAME' => Name::class,
AttributeKey::class . '::CLASS_NODE' => ClassLike::class,
@ -86,11 +86,11 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
return $returnType;
}
if (! isset($this->argumentKeyToReturnType[$argumentValue])) {
if (! isset(self::ARGUMENT_KEY_TO_RETURN_TYPE[$argumentValue])) {
return $returnType;
}
$knownReturnType = $this->argumentKeyToReturnType[$argumentValue];
$knownReturnType = self::ARGUMENT_KEY_TO_RETURN_TYPE[$argumentValue];
if ($knownReturnType === 'string') {
return new UnionType([new StringType(), new NullType()]);
}

View File

@ -16,13 +16,13 @@ use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\NodeContainer\ParsedNodesByType;
use Rector\NodeCollector\NodeCollector\ParsedNodeCollector;
final class ParsedNodesByTypeReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass(): string
{
return ParsedNodesByType::class;
return ParsedNodeCollector::class;
}
public function isMethodSupported(MethodReflection $methodReflection): bool
@ -47,10 +47,8 @@ final class ParsedNodesByTypeReturnTypeExtension implements DynamicMethodReturnT
private function resolveArgumentValue(Expr $expr): ?string
{
if ($expr instanceof ClassConstFetch) {
if ($expr->class instanceof Name) {
return $expr->class->toString();
}
if ($expr instanceof ClassConstFetch && $expr->class instanceof Name) {
return $expr->class->toString();
}
return null;

View File

@ -68,11 +68,7 @@ final class PreventParentMethodVisibilityOverrideRule implements Rule
return true;
}
if ($reflectionMethod->isPrivate() && $classMethod->isPrivate()) {
return true;
}
return false;
return $reflectionMethod->isPrivate() && $classMethod->isPrivate();
}
private function resolveReflectionMethodVisibilityAsStrings(ReflectionMethod $reflectionMethod): string