├── .github
└── workflows
│ ├── code_analysis.yaml
│ ├── code_analysis_reuslabe.yaml
│ └── tests.yaml
├── LICENSE
├── README.md
├── composer.json
├── config
└── sets
│ ├── annotations-to-attributes.php
│ ├── phpunit-code-quality.php
│ ├── phpunit100.php
│ ├── phpunit110.php
│ ├── phpunit120.php
│ ├── phpunit40.php
│ ├── phpunit50.php
│ ├── phpunit60.php
│ ├── phpunit70.php
│ ├── phpunit80.php
│ └── phpunit90.php
├── rules
├── AnnotationsToAttributes
│ └── Rector
│ │ ├── ClassMethod
│ │ ├── DataProviderAnnotationToAttributeRector.php
│ │ ├── DependsAnnotationWithValueToAttributeRector.php
│ │ └── TestWithAnnotationToAttributeRector.php
│ │ └── Class_
│ │ ├── AnnotationWithValueToAttributeRector.php
│ │ ├── CoversAnnotationWithValueToAttributeRector.php
│ │ ├── RequiresAnnotationWithValueToAttributeRector.php
│ │ └── TicketAnnotationToAttributeRector.php
├── CodeQuality
│ ├── Enum
│ │ └── NonAssertNonStaticMethods.php
│ ├── NodeAnalyser
│ │ ├── AssertMethodAnalyzer.php
│ │ ├── DoctrineEntityDocumentAnalyser.php
│ │ ├── NullableObjectAssignCollector.php
│ │ └── SetUpAssignedMockTypesResolver.php
│ ├── NodeFactory
│ │ └── NestedClosureAssertFactory.php
│ ├── Rector
│ │ ├── ClassMethod
│ │ │ ├── AddInstanceofAssertForNullableInstanceRector.php
│ │ │ ├── CreateMockToAnonymousClassRector.php
│ │ │ ├── DataProviderArrayItemsNewLinedRector.php
│ │ │ ├── EntityDocumentCreateMockToDirectNewRector.php
│ │ │ ├── RemoveEmptyTestMethodRector.php
│ │ │ ├── ReplaceTestAnnotationWithPrefixedFunctionRector.php
│ │ │ └── ReplaceTestFunctionPrefixWithAttributeRector.php
│ │ ├── Class_
│ │ │ ├── AddCoversClassAttributeRector.php
│ │ │ ├── AddParentSetupCallOnSetupRector.php
│ │ │ ├── AddSeeTestAnnotationRector.php
│ │ │ ├── ConstructClassMethodToSetUpTestCaseRector.php
│ │ │ ├── NarrowUnusedSetUpDefinedPropertyRector.php
│ │ │ ├── PreferPHPUnitSelfCallRector.php
│ │ │ ├── PreferPHPUnitThisCallRector.php
│ │ │ ├── RemoveDataProviderParamKeysRector.php
│ │ │ ├── SetUpBeforeClassToSetUpRector.php
│ │ │ ├── SingleMockPropertyTypeRector.php
│ │ │ ├── TestWithToDataProviderRector.php
│ │ │ ├── TypeWillReturnCallableArrowFunctionRector.php
│ │ │ └── YieldDataProviderRector.php
│ │ ├── Foreach_
│ │ │ └── SimplifyForeachInstanceOfRector.php
│ │ ├── FuncCall
│ │ │ └── AssertFuncCallToPHPUnitAssertRector.php
│ │ └── MethodCall
│ │ │ ├── AssertCompareOnCountableWithMethodToAssertCountRector.php
│ │ │ ├── AssertComparisonToSpecificMethodRector.php
│ │ │ ├── AssertEmptyNullableObjectToAssertInstanceofRector.php
│ │ │ ├── AssertEqualsOrAssertSameFloatParameterToSpecificMethodsTypeRector.php
│ │ │ ├── AssertEqualsToSameRector.php
│ │ │ ├── AssertFalseStrposToContainsRector.php
│ │ │ ├── AssertInstanceOfComparisonRector.php
│ │ │ ├── AssertIssetToSpecificMethodRector.php
│ │ │ ├── AssertNotOperatorRector.php
│ │ │ ├── AssertPropertyExistsRector.php
│ │ │ ├── AssertRegExpRector.php
│ │ │ ├── AssertSameBoolNullToSpecificMethodRector.php
│ │ │ ├── AssertSameTrueFalseToAssertTrueFalseRector.php
│ │ │ ├── AssertTrueFalseToSpecificMethodRector.php
│ │ │ ├── FlipAssertRector.php
│ │ │ ├── NarrowIdenticalWithConsecutiveRector.php
│ │ │ ├── NarrowSingleWillReturnCallbackRector.php
│ │ │ ├── RemoveExpectAnyFromMockRector.php
│ │ │ ├── SingleWithConsecutiveToWithRector.php
│ │ │ ├── UseSpecificWillMethodRector.php
│ │ │ └── UseSpecificWithMethodRector.php
│ ├── Reflection
│ │ └── MethodParametersAndReturnTypesResolver.php
│ └── ValueObject
│ │ ├── MatchAndReturnMatch.php
│ │ ├── ParamTypesAndReturnType.php
│ │ ├── VariableNameToType.php
│ │ └── VariableNameToTypeCollection.php
├── PHPUnit100
│ ├── NodeDecorator
│ │ └── WillReturnIfNodeDecorator.php
│ ├── NodeFactory
│ │ └── WillReturnCallbackFactory.php
│ └── Rector
│ │ ├── Class_
│ │ ├── AddProphecyTraitRector.php
│ │ ├── ParentTestClassConstructorRector.php
│ │ ├── PublicDataProviderClassMethodRector.php
│ │ ├── RemoveNamedArgsInDataProviderRector.php
│ │ └── StaticDataProviderClassMethodRector.php
│ │ ├── MethodCall
│ │ ├── PropertyExistsWithoutAssertRector.php
│ │ └── RemoveSetMethodsMethodCallRector.php
│ │ └── StmtsAwareInterface
│ │ ├── ExpectsMethodCallDecorator.php
│ │ └── WithConsecutiveRector.php
├── PHPUnit110
│ └── Rector
│ │ └── Class_
│ │ └── NamedArgumentForDataProviderRector.php
├── PHPUnit120
│ └── Rector
│ │ └── Class_
│ │ └── RemoveOverrideFinalConstructTestCaseRector.php
├── PHPUnit50
│ └── Rector
│ │ └── StaticCall
│ │ └── GetMockRector.php
├── PHPUnit60
│ └── Rector
│ │ ├── ClassMethod
│ │ ├── AddDoesNotPerformAssertionToNonAssertingTestRector.php
│ │ └── ExceptionAnnotationRector.php
│ │ └── MethodCall
│ │ ├── DelegateExceptionArgumentsRector.php
│ │ └── GetMockBuilderGetMockToCreateMockRector.php
├── PHPUnit70
│ └── Rector
│ │ └── Class_
│ │ └── RemoveDataProviderTestPrefixRector.php
├── PHPUnit80
│ └── Rector
│ │ └── MethodCall
│ │ ├── AssertEqualsParameterToSpecificMethodsTypeRector.php
│ │ ├── SpecificAssertContainsRector.php
│ │ └── SpecificAssertInternalTypeRector.php
└── PHPUnit90
│ └── Rector
│ ├── Class_
│ └── TestListenerToHooksRector.php
│ └── MethodCall
│ ├── ExplicitPhpErrorApiRector.php
│ ├── ReplaceAtMethodWithDesiredMatcherRector.php
│ └── SpecificAssertContainsWithoutIdentityRector.php
└── src
├── Composer
└── ProjectPackageVersionResolver.php
├── Enum
├── AssertMethod.php
├── ConsecutiveMethodName.php
├── ConsecutiveVariable.php
└── PHPUnitClassName.php
├── MethodCallRemover.php
├── Naming
└── TestClassNameResolver.php
├── NodeAnalyzer
├── ArgumentMover.php
├── AssertCallAnalyzer.php
├── IdentifierManipulator.php
├── MockedVariableAnalyzer.php
├── SetUpMethodDecorator.php
└── TestsNodeAnalyzer.php
├── NodeFactory
├── AssertCallFactory.php
├── ConsecutiveIfsFactory.php
├── ExpectExceptionMethodCallFactory.php
├── MatcherInvocationCountMethodCallNodeFactory.php
└── UsedVariablesResolver.php
├── NodeFinder
├── DataProviderClassMethodFinder.php
└── MethodCallNodeFinder.php
├── PhpDoc
├── DataProviderMethodRenamer.php
└── PhpDocValueToNodeMapper.php
├── Set
├── PHPUnitSetList.php
└── SetProvider
│ └── PHPUnitSetProvider.php
└── ValueObject
├── AnnotationWithValueToAttribute.php
├── BinaryOpWithAssertMethod.php
├── ConstantWithAssertMethods.php
└── FunctionNameWithAssertMethods.php
/.github/workflows/code_analysis.yaml:
--------------------------------------------------------------------------------
1 | name: Code Analysis
2 |
3 | on:
4 | pull_request: null
5 | push:
6 | branches:
7 | - main
8 |
9 | env:
10 | # see https://github.com/composer/composer/issues/9368#issuecomment-718112361
11 | COMPOSER_ROOT_VERSION: "dev-main"
12 |
13 | jobs:
14 | code_analysis:
15 | strategy:
16 | fail-fast: false
17 | matrix:
18 | actions:
19 | -
20 | name: 'Active Classes'
21 | run: composer class-leak
22 |
23 | -
24 | name: 'Finalise classes'
25 | run: vendor/bin/swiss-knife finalize-classes src rules tests
26 |
27 | name: ${{ matrix.actions.name }}
28 | runs-on: ubuntu-latest
29 | timeout-minutes: 10
30 |
31 | steps:
32 | - uses: actions/checkout@v4
33 |
34 | -
35 | uses: shivammathur/setup-php@v2
36 | with:
37 | php-version: 8.2
38 | coverage: none
39 |
40 | - uses: "ramsey/composer-install@v2"
41 | - run: ${{ matrix.actions.run }}
42 |
--------------------------------------------------------------------------------
/.github/workflows/code_analysis_reuslabe.yaml:
--------------------------------------------------------------------------------
1 | name: Code Analysis Reusable
2 |
3 | on:
4 | pull_request: null
5 | push:
6 | branches:
7 | - main
8 |
9 | jobs:
10 | code_analysis_reusable:
11 | # see https://github.com/rectorphp/reusable-workflows
12 | uses: rectorphp/reusable-workflows/.github/workflows/code_analysis.yaml@main
13 |
14 | rector:
15 | # run only on main repository, not on the forks without access
16 | if: github.repository == 'rectorphp/rector-phpunit'
17 |
18 | # see https://github.com/rectorphp/reusable-workflows
19 | uses: rectorphp/reusable-workflows/.github/workflows/rector.yaml@main
20 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | pull_request: null
5 | push:
6 | branches:
7 | - main
8 |
9 | env:
10 | # see https://github.com/composer/composer/issues/9368#issuecomment-718112361
11 | COMPOSER_ROOT_VERSION: "dev-main"
12 |
13 | jobs:
14 | tests:
15 | runs-on: ubuntu-latest
16 |
17 | name: Tests
18 | steps:
19 | - uses: actions/checkout@v4
20 |
21 | -
22 | uses: shivammathur/setup-php@v2
23 | with:
24 | php-version: 8.2
25 | coverage: none
26 |
27 | - uses: "ramsey/composer-install@v2"
28 |
29 | - run: vendor/bin/phpunit
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 | ---------------
3 |
4 | Copyright (c) 2017-present Tomáš Votruba (https://tomasvotruba.cz)
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the "Software"), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rector Rules for PHPUnit
2 |
3 | See available [PHPUnit rules](https://getrector.com/find-rule?activeRectorSetGroup=phpunit)
4 |
5 | ## Install
6 |
7 | This package is already part of [rector/rector](http://github.com/rectorphp/rector) package, so it works out of the box.
8 |
9 | All you need to do is install the main package, and you're good to go:
10 |
11 | ```bash
12 | composer require rector/rector --dev
13 | ```
14 |
15 | ## Use Sets
16 |
17 | To add a set to your config, use `Rector\PHPUnit\Set\PHPUnitSetList` class and pick one of constants:
18 |
19 | ```php
20 | use Rector\Config\RectorConfig;
21 | use Rector\PHPUnit\Set\PHPUnitSetList;
22 |
23 | return RectorConfig::configure()
24 | ->withSets([
25 | PHPUnitSetList::PHPUNIT_90,
26 | ]);
27 | ```
28 |
29 |
30 |
31 | ## Learn Rector Faster
32 |
33 | Rector is a tool that [we develop](https://getrector.org/) and share for free, so anyone can save hundreds of hours on refactoring. But not everyone has time to understand Rector and AST complexity. You have 2 ways to speed this process up:
34 |
35 | * read a book - The Power of Automated Refactoring
36 | * hire our experienced team to improve your code base
37 |
38 | Both ways support us to and improve Rector in sustainable way by learning from practical projects.
39 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rector/rector-phpunit",
3 | "type": "rector-extension",
4 | "license": "MIT",
5 | "description": "Rector upgrades rules for PHPUnit",
6 | "require": {
7 | "php": ">=8.2"
8 | },
9 | "require-dev": {
10 | "phpecs/phpecs": "^2.1.1",
11 | "phpstan/extension-installer": "^1.4",
12 | "phpstan/phpstan": "^2.1.8",
13 | "phpstan/phpstan-deprecation-rules": "^2.0",
14 | "phpstan/phpstan-webmozart-assert": "^2.0",
15 | "phpunit/phpunit": "^11.5",
16 | "rector/rector-src": "dev-main",
17 | "rector/swiss-knife": "^1.0",
18 | "rector/type-perfect": "^2.0",
19 | "symplify/phpstan-extensions": "^12.0",
20 | "symplify/vendor-patches": "^11.4",
21 | "tomasvotruba/class-leak": "^1.2",
22 | "tracy/tracy": "^2.10"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "Rector\\PHPUnit\\": ["src", "rules"]
27 | }
28 | },
29 | "autoload-dev": {
30 | "psr-4": {
31 | "Rector\\PHPUnit\\Tests\\": ["tests", "rules-tests"]
32 | },
33 | "classmap": [
34 | "stubs"
35 | ]
36 | },
37 | "scripts": {
38 | "complete-check": [
39 | "@check-cs",
40 | "@class-leak",
41 | "@phpstan",
42 | "@rector",
43 | "@docs",
44 | "phpunit"
45 | ],
46 | "phpstan": "vendor/bin/phpstan analyse --ansi",
47 | "check-cs": "vendor/bin/ecs check --ansi",
48 | "class-leak": "vendor/bin/class-leak check config src rules --skip-suffix \"Rector\"",
49 | "fix-cs": "vendor/bin/ecs check --fix --ansi",
50 | "rector": "vendor/bin/rector process --ansi"
51 | },
52 | "extra": {
53 | "enable-patching": true
54 | },
55 | "conflict": {
56 | "rector/rector": "<2.0"
57 | },
58 | "minimum-stability": "dev",
59 | "prefer-stable": true,
60 | "config": {
61 | "sort-packages": true,
62 | "allow-plugins": {
63 | "cweagans/composer-patches": true,
64 | "rector/extension-installer": true,
65 | "phpstan/extension-installer": true
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/config/sets/phpunit110.php:
--------------------------------------------------------------------------------
1 | rule(NamedArgumentForDataProviderRector::class);
10 | };
11 |
--------------------------------------------------------------------------------
/config/sets/phpunit120.php:
--------------------------------------------------------------------------------
1 | rule(RemoveOverrideFinalConstructTestCaseRector::class);
10 | };
11 |
--------------------------------------------------------------------------------
/config/sets/phpunit40.php:
--------------------------------------------------------------------------------
1 | ruleWithConfiguration(RenameMethodRector::class, [
11 | new MethodCallRename(
12 | 'PHPUnit_Framework_MockObject_MockObject',
13 | # see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/137
14 | 'staticExpects',
15 | 'expects'
16 | ),
17 | ]);
18 | };
19 |
--------------------------------------------------------------------------------
/config/sets/phpunit50.php:
--------------------------------------------------------------------------------
1 | rule(GetMockRector::class);
10 | };
11 |
--------------------------------------------------------------------------------
/config/sets/phpunit70.php:
--------------------------------------------------------------------------------
1 | ruleWithConfiguration(RenameAnnotationRector::class, [
12 | new RenameAnnotationByType('PHPUnit\Framework\TestCase', 'scenario', 'test'),
13 | ]);
14 |
15 | $rectorConfig->rule(RemoveDataProviderTestPrefixRector::class);
16 | };
17 |
--------------------------------------------------------------------------------
/config/sets/phpunit80.php:
--------------------------------------------------------------------------------
1 | rules([
20 | SpecificAssertInternalTypeRector::class,
21 | AssertEqualsParameterToSpecificMethodsTypeRector::class,
22 | SpecificAssertContainsRector::class,
23 | ]);
24 |
25 | $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [
26 | # https://github.com/sebastianbergmann/phpunit/issues/3123
27 | 'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject',
28 | ]);
29 |
30 | $rectorConfig->ruleWithConfiguration(AddParamTypeDeclarationRector::class, [
31 | // https://github.com/rectorphp/rector/issues/1024 - no type, $dataName
32 | new AddParamTypeDeclaration('PHPUnit\Framework\TestCase', MethodName::CONSTRUCT, 2, new MixedType()),
33 | ]);
34 |
35 | $rectorConfig->ruleWithConfiguration(AddReturnTypeDeclarationRector::class, [
36 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'setUpBeforeClass', new VoidType()),
37 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'setUp', new VoidType()),
38 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'assertPreConditions', new VoidType()),
39 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'assertPostConditions', new VoidType()),
40 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'tearDown', new VoidType()),
41 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'tearDownAfterClass', new VoidType()),
42 | new AddReturnTypeDeclaration('PHPUnit\Framework\TestCase', 'onNotSuccessfulTest', new VoidType()),
43 | ]);
44 | };
45 |
--------------------------------------------------------------------------------
/config/sets/phpunit90.php:
--------------------------------------------------------------------------------
1 | rules([
15 | TestListenerToHooksRector::class,
16 | ExplicitPhpErrorApiRector::class,
17 | SpecificAssertContainsWithoutIdentityRector::class,
18 | WithConsecutiveRector::class,
19 | ]);
20 |
21 | $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
22 | // see https://github.com/sebastianbergmann/phpunit/issues/3957
23 | new MethodCallRename(
24 | 'PHPUnit\Framework\TestCase',
25 | 'expectExceptionMessageRegExp',
26 | 'expectExceptionMessageMatches'
27 | ),
28 | ]);
29 | };
30 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Enum/NonAssertNonStaticMethods.php:
--------------------------------------------------------------------------------
1 | var
31 | : $call->class;
32 |
33 | if (! $this->nodeTypeResolver->isObjectType($objectCaller, new ObjectType('PHPUnit\Framework\TestCase'))) {
34 | return false;
35 | }
36 |
37 | $methodName = $this->nodeNameResolver->getName($call->name);
38 | if (! str_starts_with((string) $methodName, 'assert') && ! in_array(
39 | $methodName,
40 | NonAssertNonStaticMethods::ALL,
41 | true
42 | )) {
43 | return false;
44 | }
45 |
46 | if ($call instanceof StaticCall && ! $this->nodeNameResolver->isNames($call->class, ['static', 'self'])) {
47 | return false;
48 | }
49 |
50 | $extendedMethodReflection = $this->resolveMethodReflection($call);
51 | if (! $extendedMethodReflection instanceof ExtendedMethodReflection) {
52 | return false;
53 | }
54 |
55 | // only handle methods in TestCase or Assert class classes
56 | $declaringClassName = $extendedMethodReflection->getDeclaringClass()
57 | ->getName();
58 |
59 | return in_array($declaringClassName, [PHPUnitClassName::TEST_CASE, PHPUnitClassName::ASSERT]);
60 | }
61 |
62 | public function detectTestCaseCallForStatic(MethodCall $methodCall): bool
63 | {
64 | if (! $this->detectTestCaseCall($methodCall)) {
65 | return false;
66 | }
67 |
68 | $extendedMethodReflection = $this->resolveMethodReflection($methodCall);
69 |
70 | return $extendedMethodReflection instanceof ExtendedMethodReflection && $extendedMethodReflection->isStatic();
71 | }
72 |
73 | private function resolveMethodReflection(MethodCall|StaticCall $call): ?ExtendedMethodReflection
74 | {
75 | $methodName = $this->nodeNameResolver->getName($call->name);
76 |
77 | $classReflection = $this->reflectionResolver->resolveClassReflection($call);
78 | if (! $classReflection instanceof ClassReflection) {
79 | return null;
80 | }
81 |
82 | return $classReflection->getNativeMethod($methodName);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/rules/CodeQuality/NodeAnalyser/DoctrineEntityDocumentAnalyser.php:
--------------------------------------------------------------------------------
1 | getResolvedPhpDoc();
20 | if (! $resolvedPhpDocBlock instanceof ResolvedPhpDocBlock) {
21 | return false;
22 | }
23 |
24 | foreach (self::ENTITY_DOCBLOCK_MARKERS as $entityDocBlockMarkers) {
25 | if (str_contains($resolvedPhpDocBlock->getPhpDocString(), $entityDocBlockMarkers)) {
26 | return true;
27 | }
28 | }
29 |
30 | // @todo apply attributes as well
31 |
32 | return false;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/rules/CodeQuality/NodeAnalyser/NullableObjectAssignCollector.php:
--------------------------------------------------------------------------------
1 | getSomething();
24 | */
25 | final readonly class NullableObjectAssignCollector
26 | {
27 | public function __construct(
28 | private NodeNameResolver $nodeNameResolver,
29 | private NodeTypeResolver $nodeTypeResolver,
30 | ) {
31 | }
32 |
33 | public function collect(ClassMethod|Foreach_ $stmtsAware): VariableNameToTypeCollection
34 | {
35 | $variableNamesToType = [];
36 |
37 | // first round to collect assigns
38 | foreach ((array) $stmtsAware->stmts as $stmt) {
39 | if (! $stmt instanceof Expression) {
40 | return new VariableNameToTypeCollection([]);
41 | }
42 |
43 | if (! $stmt->expr instanceof Assign) {
44 | continue;
45 | }
46 |
47 | $variableNameToType = $this->collectFromAssign($stmt->expr);
48 | if (! $variableNameToType instanceof VariableNameToType) {
49 | continue;
50 | }
51 |
52 | $variableNamesToType[] = $variableNameToType;
53 | }
54 |
55 | return new VariableNameToTypeCollection($variableNamesToType);
56 | }
57 |
58 | private function collectFromAssign(Assign $assign): ?VariableNameToType
59 | {
60 | if (! $assign->expr instanceof MethodCall) {
61 | return null;
62 | }
63 |
64 | if (! $assign->var instanceof Variable) {
65 | return null;
66 | }
67 |
68 | $variableType = $this->nodeTypeResolver->getType($assign);
69 |
70 | $bareVariableType = TypeCombinator::removeNull($variableType);
71 | if (! $bareVariableType instanceof ObjectType) {
72 | return null;
73 | }
74 |
75 | $variableName = $this->nodeNameResolver->getName($assign->var);
76 | return new VariableNameToType($variableName, $bareVariableType->getClassName());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/rules/CodeQuality/NodeAnalyser/SetUpAssignedMockTypesResolver.php:
--------------------------------------------------------------------------------
1 |
28 | */
29 | public function resolveFromClass(Class_ $class): array
30 | {
31 | $setUpClassMethod = $class->getMethod(MethodName::SET_UP);
32 | if (! $setUpClassMethod instanceof ClassMethod) {
33 | return [];
34 | }
35 |
36 | $propertyNameToMockedTypes = [];
37 | foreach ((array) $setUpClassMethod->stmts as $stmt) {
38 | if (! $stmt instanceof Expression) {
39 | continue;
40 | }
41 |
42 | if (! $stmt->expr instanceof Assign) {
43 | continue;
44 | }
45 |
46 | $assign = $stmt->expr;
47 | if (! $assign->expr instanceof MethodCall) {
48 | continue;
49 | }
50 |
51 | if (! $this->nodeNameResolver->isNames($assign->expr->name, ['createMock', 'getMockBuilder'])) {
52 | continue;
53 | }
54 |
55 | if (! $assign->var instanceof PropertyFetch && ! $assign->var instanceof Variable) {
56 | continue;
57 | }
58 |
59 | $mockedClassNameExpr = $assign->expr->getArgs()[0]
60 | ->value;
61 | if (! $mockedClassNameExpr instanceof ClassConstFetch) {
62 | continue;
63 | }
64 |
65 | $propertyOrVariableName = $this->resolvePropertyOrVariableName($assign->var);
66 | $mockedClass = $this->nodeNameResolver->getName($mockedClassNameExpr->class);
67 |
68 | Assert::string($mockedClass);
69 |
70 | $propertyNameToMockedTypes[$propertyOrVariableName] = $mockedClass;
71 | }
72 |
73 | return $propertyNameToMockedTypes;
74 | }
75 |
76 | private function resolvePropertyOrVariableName(PropertyFetch|Variable $propertyFetchOrVariable): ?string
77 | {
78 | if ($propertyFetchOrVariable instanceof Variable) {
79 | return $this->nodeNameResolver->getName($propertyFetchOrVariable);
80 | }
81 |
82 | return $this->nodeNameResolver->getName($propertyFetchOrVariable->name);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/rules/CodeQuality/NodeFactory/NestedClosureAssertFactory.php:
--------------------------------------------------------------------------------
1 | getArgs()[0];
32 |
33 | if ($callableFirstArg->value instanceof ArrowFunction) {
34 | $arrowFunction = $callableFirstArg->value;
35 | if ($arrowFunction->expr instanceof Identical) {
36 | // unwrap closure arrow function to direct assert as more readalbe
37 | $identical = $arrowFunction->expr;
38 |
39 | if ($identical->left instanceof Variable) {
40 | return $this->createAssertSameParameters($identical->right, $assertKey);
41 | }
42 |
43 | if ($identical->right instanceof Variable) {
44 | return $this->createAssertSameParameters($identical->left, $assertKey);
45 | }
46 | }
47 |
48 | if ($arrowFunction->expr instanceof BooleanNot && $arrowFunction->expr->expr instanceof Empty_) {
49 | return $this->createAssertNotEmpty($assertKey, 'assertNotEmpty');
50 | }
51 |
52 | if ($arrowFunction->expr instanceof Empty_) {
53 | return $this->createAssertNotEmpty($assertKey, 'assertEmpty');
54 | }
55 | }
56 |
57 | $callbackVariable = new Variable('callback');
58 | $callbackAssign = new Assign($callbackVariable, $callableFirstArg->value);
59 |
60 | $stmts = [new Expression($callbackAssign)];
61 |
62 | $parametersArrayDimFetch = new ArrayDimFetch(new Variable('parameters'), new Int_($assertKey));
63 | $callbackFuncCall = new FuncCall($callbackVariable, [new Arg($parametersArrayDimFetch)]);
64 |
65 | // add assert true to the callback
66 | $assertTrueMethodCall = new MethodCall(new Variable('this'), 'assertTrue', [new Arg($callbackFuncCall)]);
67 | $stmts[] = new Expression($assertTrueMethodCall);
68 |
69 | return $stmts;
70 | }
71 |
72 | /**
73 | * @return Expression[]
74 | */
75 | private function createAssertSameParameters(Expr $comparedExpr, int $assertKey): array
76 | {
77 | // use assert same directly instead
78 | $args = [
79 | new Arg($comparedExpr),
80 | new Arg(new ArrayDimFetch(new Variable('parameters'), new Int_($assertKey))),
81 | ];
82 |
83 | $assertSameMethodCall = new MethodCall(new Variable('this'), new Identifier('assertSame'), $args);
84 |
85 | return [new Expression($assertSameMethodCall)];
86 | }
87 |
88 | /**
89 | * @return Expression[]
90 | */
91 | private function createAssertNotEmpty(int $assertKey, string $emptyMethodName): array
92 | {
93 | $arrayDimFetch = new ArrayDimFetch(new Variable(ConsecutiveVariable::PARAMETERS), new Int_($assertKey));
94 |
95 | $assertEmptyMethodCall = new MethodCall(new Variable('this'), new Identifier($emptyMethodName), [
96 | new Arg($arrayDimFetch),
97 | ]);
98 |
99 | return [new Expression($assertEmptyMethodCall)];
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/ClassMethod/DataProviderArrayItemsNewLinedRector.php:
--------------------------------------------------------------------------------
1 | >
80 | */
81 | public function getNodeTypes(): array
82 | {
83 | return [ClassMethod::class];
84 | }
85 |
86 | /**
87 | * @param ClassMethod $node
88 | */
89 | public function refactor(Node $node): ?Node
90 | {
91 | if (! $node->isPublic()) {
92 | return null;
93 | }
94 |
95 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
96 | return null;
97 | }
98 |
99 | // skip test methods
100 | if (str_starts_with($node->name->toString(), 'test')) {
101 | return null;
102 | }
103 |
104 | // find array in data provider - must contain a return node
105 | /** @var Return_[] $returns */
106 | $returns = $this->betterNodeFinder->findInstanceOf((array) $node->stmts, Return_::class);
107 | $hasChanged = \false;
108 | foreach ($returns as $return) {
109 | if (! $return->expr instanceof Array_) {
110 | continue;
111 | }
112 |
113 | $array = $return->expr;
114 | if ($array->items === []) {
115 | continue;
116 | }
117 |
118 | if (! $this->shouldRePrint($array)) {
119 | continue;
120 | }
121 |
122 | // ensure newlined printed
123 | $array->setAttribute(AttributeKey::NEWLINED_ARRAY_PRINT, \true);
124 | // invoke reprint
125 | $array->setAttribute(AttributeKey::ORIGINAL_NODE, null);
126 | $hasChanged = \true;
127 | }
128 |
129 | if ($hasChanged) {
130 | return $node;
131 | }
132 |
133 | return null;
134 | }
135 |
136 | private function shouldRePrint(Array_ $array): bool
137 | {
138 | foreach ($array->items as $key => $item) {
139 | if (! $item instanceof ArrayItem) {
140 | continue;
141 | }
142 |
143 | if (! isset($array->items[$key + 1])) {
144 | continue;
145 | }
146 |
147 | if ($array->items[$key + 1]->getStartLine() !== $item->getEndLine()) {
148 | continue;
149 | }
150 |
151 | return true;
152 | }
153 |
154 | return false;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/ClassMethod/RemoveEmptyTestMethodRector.php:
--------------------------------------------------------------------------------
1 | >
54 | */
55 | public function getNodeTypes(): array
56 | {
57 | return [ClassMethod::class];
58 | }
59 |
60 | /**
61 | * @param ClassMethod $node
62 | */
63 | public function refactor(Node $node): ?int
64 | {
65 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
66 | return null;
67 | }
68 |
69 | if (! str_starts_with($node->name->toString(), 'test')) {
70 | return null;
71 | }
72 |
73 | if ($node->stmts === null) {
74 | return null;
75 | }
76 |
77 | if ($node->stmts !== []) {
78 | return null;
79 | }
80 |
81 | return NodeVisitor::REMOVE_NODE;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/ClassMethod/ReplaceTestAnnotationWithPrefixedFunctionRector.php:
--------------------------------------------------------------------------------
1 | assertSame(2, 1+1);
44 | }
45 | }
46 | CODE_SAMPLE
47 | ,
48 | <<<'CODE_SAMPLE'
49 | class SomeTest extends \PHPUnit\Framework\TestCase
50 | {
51 | public function testOnePlusOneShouldBeTwo()
52 | {
53 | $this->assertSame(2, 1+1);
54 | }
55 | }
56 | CODE_SAMPLE
57 | ),
58 | ]);
59 | }
60 |
61 | /**
62 | * @return array>
63 | */
64 | public function getNodeTypes(): array
65 | {
66 | return [ClassMethod::class];
67 | }
68 |
69 | /**
70 | * @param ClassMethod $node
71 | */
72 | public function refactor(Node $node): ?Node
73 | {
74 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
75 | return null;
76 | }
77 |
78 | if (str_starts_with($node->name->toString(), 'test')) {
79 | return null;
80 | }
81 |
82 | $docComment = $node->getDocComment();
83 | if (! $docComment instanceof Doc) {
84 | return null;
85 | }
86 |
87 | if (! str_contains($docComment->getText(), '@test')) {
88 | return null;
89 | }
90 |
91 | $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
92 | $this->phpDocTagRemover->removeByName($phpDocInfo, 'test');
93 | $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
94 |
95 | $node->name->name = 'test' . ucfirst($node->name->name);
96 |
97 | return $node;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/ClassMethod/ReplaceTestFunctionPrefixWithAttributeRector.php:
--------------------------------------------------------------------------------
1 | assertSame(2, 1+1);
39 | }
40 | }
41 | CODE_SAMPLE
42 | ,
43 | <<<'CODE_SAMPLE'
44 | class SomeTest extends \PHPUnit\Framework\TestCase
45 | {
46 | #[Test]
47 | public function onePlusOneShouldBeTwo()
48 | {
49 | $this->assertSame(2, 1+1);
50 | }
51 | }
52 | CODE_SAMPLE
53 | ),
54 | ]);
55 | }
56 |
57 | /**
58 | * @return array>
59 | */
60 | public function getNodeTypes(): array
61 | {
62 | return [ClassMethod::class];
63 | }
64 |
65 | /**
66 | * @param ClassMethod $node
67 | */
68 | public function refactor(Node $node): ?Node
69 | {
70 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
71 | return null;
72 | }
73 |
74 | if (! str_starts_with($node->name->toString(), 'test')) {
75 | return null;
76 | }
77 |
78 | if ($this->phpAttributeAnalyzer->hasPhpAttributes($node, ['PHPUnit\\Framework\\Attributes\\Test'])) {
79 | return null;
80 | }
81 |
82 | if ($node->name->toString() !== 'test' && $node->name->toString() !== 'test_') {
83 | if (str_starts_with($node->name->toString(), 'test_')) {
84 | $node->name->name = lcfirst(substr($node->name->name, 5));
85 | } elseif (str_starts_with($node->name->toString(), 'test')) {
86 | $node->name->name = lcfirst(substr($node->name->name, 4));
87 | }
88 | }
89 |
90 | $coversAttributeGroup = $this->createAttributeGroup();
91 | $node->attrGroups = array_merge($node->attrGroups, [$coversAttributeGroup]);
92 |
93 | return $node;
94 | }
95 |
96 | private function createAttributeGroup(): AttributeGroup
97 | {
98 | $attributeClass = 'PHPUnit\\Framework\\Attributes\\Test';
99 |
100 | return $this->phpAttributeGroupFactory->createFromClassWithItems($attributeClass, []);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/Class_/AddParentSetupCallOnSetupRector.php:
--------------------------------------------------------------------------------
1 | >
68 | */
69 | public function getNodeTypes(): array
70 | {
71 | return [Class_::class];
72 | }
73 |
74 | /**
75 | * @param Class_ $node
76 | */
77 | public function refactor(Node $node): ?Node
78 | {
79 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
80 | return null;
81 | }
82 |
83 | $setUpMethod = $node->getMethod(MethodName::SET_UP);
84 | if (! $setUpMethod instanceof ClassMethod) {
85 | return null;
86 | }
87 |
88 | if ($setUpMethod->isAbstract() || $setUpMethod->stmts === null) {
89 | return null;
90 | }
91 |
92 | $isSetupExists = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped(
93 | $setUpMethod,
94 | function (Node $subNode): bool {
95 | if (! $subNode instanceof StaticCall) {
96 | return false;
97 | }
98 |
99 | if (! $this->isName($subNode->class, 'parent')) {
100 | return false;
101 | }
102 |
103 | return $this->isName($subNode->name, 'setUp');
104 | }
105 | );
106 |
107 | if ($isSetupExists) {
108 | return null;
109 | }
110 |
111 | $setUpMethod->stmts = [
112 | new Expression(new StaticCall(new Name('parent'), new Identifier('setUp'))),
113 | ...$setUpMethod->stmts,
114 | ];
115 |
116 | return $node;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/Class_/PreferPHPUnitSelfCallRector.php:
--------------------------------------------------------------------------------
1 | assert*() to self::assert*()', [
31 | new CodeSample(
32 | <<<'CODE_SAMPLE'
33 | use PHPUnit\Framework\TestCase;
34 |
35 | final class SomeClass extends TestCase
36 | {
37 | public function run()
38 | {
39 | $this->assertEquals('expected', $result);
40 | }
41 | }
42 | CODE_SAMPLE
43 | ,
44 | <<<'CODE_SAMPLE'
45 | use PHPUnit\Framework\TestCase;
46 |
47 | final class SomeClass extends TestCase
48 | {
49 | public function run()
50 | {
51 | self::assertEquals('expected', $result);
52 | }
53 | }
54 | CODE_SAMPLE
55 | ),
56 | ]);
57 | }
58 |
59 | /**
60 | * @return array>
61 | */
62 | public function getNodeTypes(): array
63 | {
64 | return [Class_::class];
65 | }
66 |
67 | /**
68 | * @param Class_ $node
69 | */
70 | public function refactor(Node $node): ?Node
71 | {
72 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
73 | return null;
74 | }
75 |
76 | $hasChanged = false;
77 | $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): ?StaticCall {
78 | if (! $node instanceof MethodCall) {
79 | return null;
80 | }
81 |
82 | if ($node->isFirstClassCallable()) {
83 | return null;
84 | }
85 |
86 | if (! $this->assertMethodAnalyzer->detectTestCaseCallForStatic($node)) {
87 | return null;
88 | }
89 |
90 | $methodName = $this->getName($node->name);
91 |
92 | $hasChanged = true;
93 | return $this->nodeFactory->createStaticCall('self', $methodName, $node->getArgs());
94 | });
95 |
96 | if ($hasChanged) {
97 | return $node;
98 | }
99 |
100 | return null;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/Class_/PreferPHPUnitThisCallRector.php:
--------------------------------------------------------------------------------
1 | assert*()', [
35 | new CodeSample(
36 | <<<'CODE_SAMPLE'
37 | use PHPUnit\Framework\TestCase;
38 |
39 | final class SomeClass extends TestCase
40 | {
41 | public function run()
42 | {
43 | self::assertEquals('expected', $result);
44 | }
45 | }
46 | CODE_SAMPLE
47 | ,
48 | <<<'CODE_SAMPLE'
49 | use PHPUnit\Framework\TestCase;
50 |
51 | final class SomeClass extends TestCase
52 | {
53 | public function run()
54 | {
55 | $this->assertEquals('expected', $result);
56 | }
57 | }
58 | CODE_SAMPLE
59 | ),
60 | ]);
61 | }
62 |
63 | /**
64 | * @return array>
65 | */
66 | public function getNodeTypes(): array
67 | {
68 | return [Class_::class];
69 | }
70 |
71 | /**
72 | * @param Class_ $node
73 | */
74 | public function refactor(Node $node): ?Node
75 | {
76 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
77 | return null;
78 | }
79 |
80 | $hasChanged = false;
81 |
82 | $this->traverseNodesWithCallable($node, function (Node $node) use (&$hasChanged): int|null|MethodCall {
83 | $isInsideStaticFunctionLike = ($node instanceof ClassMethod && $node->isStatic()) || (($node instanceof Closure || $node instanceof ArrowFunction) && $node->static);
84 | if ($isInsideStaticFunctionLike) {
85 | return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
86 | }
87 |
88 | if (! $node instanceof StaticCall) {
89 | return null;
90 | }
91 |
92 | if ($node->isFirstClassCallable()) {
93 | return null;
94 | }
95 |
96 | if (! $this->assertMethodAnalyzer->detectTestCaseCall($node)) {
97 | return null;
98 | }
99 |
100 | $methodName = $this->getName($node->name);
101 |
102 | $hasChanged = true;
103 | return $this->nodeFactory->createMethodCall('this', $methodName, $node->getArgs());
104 | });
105 |
106 | if ($hasChanged) {
107 | return $node;
108 | }
109 |
110 | return null;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/Class_/SingleMockPropertyTypeRector.php:
--------------------------------------------------------------------------------
1 | someEntityMock = $this->createMock(SimpleObject::class);
44 | }
45 | }
46 | CODE_SAMPLE
47 | ,
48 | <<<'CODE_SAMPLE'
49 | use PHPUnit\Framework\TestCase;
50 | use PHPUnit\Framework\MockObject\MockObject;
51 |
52 | final class MockingEntity extends TestCase
53 | {
54 | private MockObject $someEntityMock;
55 |
56 | protected function setUp(): void
57 | {
58 | $this->someEntityMock = $this->createMock(SimpleObject::class);
59 | }
60 | }
61 | CODE_SAMPLE
62 | ),
63 | ]
64 | );
65 | }
66 |
67 | /**
68 | * @return array>
69 | */
70 | public function getNodeTypes(): array
71 | {
72 | return [Class_::class];
73 | }
74 |
75 | /**
76 | * @param Class_ $node
77 | */
78 | public function refactor(Node $node): ?Class_
79 | {
80 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
81 | return null;
82 | }
83 |
84 | $hasChanged = false;
85 |
86 | foreach ($node->getProperties() as $property) {
87 | if (! $property->type instanceof IntersectionType && ! $property->type instanceof UnionType) {
88 | continue;
89 | }
90 |
91 | $complexType = $property->type;
92 | if (count($complexType->types) !== 2) {
93 | continue;
94 | }
95 |
96 | foreach ($complexType->types as $intersectionType) {
97 | if ($this->isName($intersectionType, MockObject::class)) {
98 | $property->type = $intersectionType;
99 | $hasChanged = true;
100 |
101 | break;
102 | }
103 | }
104 | }
105 |
106 | if (! $hasChanged) {
107 | return null;
108 | }
109 |
110 | return $node;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/Foreach_/SimplifyForeachInstanceOfRector.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(SplFileInfo::class, $foo);
31 | }
32 | CODE_SAMPLE
33 | ,
34 | <<<'CODE_SAMPLE'
35 | $this->assertContainsOnlyInstancesOf(\SplFileInfo::class, $foos);
36 | CODE_SAMPLE
37 | ),
38 | ]
39 | );
40 | }
41 |
42 | /**
43 | * @return array>
44 | */
45 | public function getNodeTypes(): array
46 | {
47 | return [Foreach_::class];
48 | }
49 |
50 | /**
51 | * @param Foreach_ $node
52 | */
53 | public function refactor(Node $node): ?Node
54 | {
55 | if (count($node->stmts) !== 1) {
56 | return null;
57 | }
58 |
59 | $onlyStmt = $node->stmts[0];
60 | if (! $onlyStmt instanceof Expression) {
61 | return null;
62 | }
63 |
64 | $expr = $onlyStmt->expr;
65 | if (! $expr instanceof MethodCall && ! $expr instanceof StaticCall) {
66 | return null;
67 | }
68 |
69 | if (! $this->isName($expr->name, 'assertInstanceOf')) {
70 | return null;
71 | }
72 |
73 | if ($expr->isFirstClassCallable()) {
74 | return null;
75 | }
76 |
77 | if (! $this->nodeComparator->areNodesEqual($node->valueVar, $expr->getArgs()[1]->value)) {
78 | return null;
79 | }
80 |
81 | // skip if there is a custom message included; it might be per item
82 | if (count($expr->getArgs()) === 3) {
83 | return null;
84 | }
85 |
86 | $newArgs = [$expr->getArgs()[0], new Arg($node->expr)];
87 |
88 | if ($expr instanceof StaticCall) {
89 | $staticCall = new StaticCall($expr->class, 'assertContainsOnlyInstancesOf', $newArgs);
90 | return new Expression($staticCall);
91 | }
92 |
93 | $methodCall = new MethodCall($expr->var, 'assertContainsOnlyInstancesOf', $newArgs);
94 | return new Expression($methodCall);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertCompareOnCountableWithMethodToAssertCountRector.php:
--------------------------------------------------------------------------------
1 | assertSame(1, $countable->count());
37 | CODE_SAMPLE
38 | ,
39 | <<<'CODE_SAMPLE'
40 | $this->assertCount(1, $countable);
41 | CODE_SAMPLE
42 | ),
43 | new CodeSample(
44 | '$this->assertSame(10, count($anything), "message");',
45 | '$this->assertCount(10, $anything, "message");'
46 | ),
47 | ]
48 | );
49 | }
50 |
51 | /**
52 | * @return array>
53 | */
54 | public function getNodeTypes(): array
55 | {
56 | return [MethodCall::class, StaticCall::class];
57 | }
58 |
59 | /**
60 | * @param MethodCall|StaticCall $node
61 | */
62 | public function refactor(Node $node): MethodCall|StaticCall|null
63 | {
64 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames(
65 | $node,
66 | ['assertSame', 'assertNotSame', 'assertEquals', 'assertNotEquals']
67 | )) {
68 | return null;
69 | }
70 |
71 | if ($node->isFirstClassCallable()) {
72 | return null;
73 | }
74 |
75 | $assertArgs = $node->getArgs();
76 | if (count($assertArgs) < 2) {
77 | return null;
78 | }
79 |
80 | $comparedExpr = $assertArgs[1]->value;
81 |
82 | if (
83 | $comparedExpr instanceof FuncCall
84 | && $this->isNames($comparedExpr->name, ['count', 'sizeof', 'iterator_count'])
85 | ) {
86 | $countArg = $comparedExpr->getArgs()[0];
87 | $assertArgs[1] = new Arg($countArg->value);
88 |
89 | $node->args = $assertArgs;
90 | $this->renameMethod($node);
91 |
92 | return $node;
93 | }
94 |
95 | if (
96 | ($comparedExpr instanceof MethodCall)
97 | && $this->isName($comparedExpr->name, 'count')
98 | && $comparedExpr->getArgs() === []
99 | ) {
100 | $type = $this->getType($comparedExpr->var);
101 |
102 | if ((new ObjectType('Countable'))->isSuperTypeOf($type)->yes()) {
103 | $args = $assertArgs;
104 | $args[1] = new Arg($comparedExpr->var);
105 |
106 | $node->args = $args;
107 | $this->renameMethod($node);
108 | }
109 | }
110 |
111 | return null;
112 | }
113 |
114 | private function renameMethod(MethodCall|StaticCall $node): void
115 | {
116 | if ($this->isNames($node->name, ['assertSame', 'assertEquals'])) {
117 | $node->name = new Identifier('assertCount');
118 | } elseif ($this->isNames($node->name, ['assertNotSame', 'assertNotEquals'])) {
119 | $node->name = new Identifier('assertNotCount');
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertEmptyNullableObjectToAssertInstanceofRector.php:
--------------------------------------------------------------------------------
1 | assertNotEmpty($someObject);
47 | }
48 | }
49 | CODE_SAMPLE
50 | ,
51 | <<<'CODE_SAMPLE'
52 | use PHPUnit\Framework\TestCase;
53 |
54 | class SomeClass extends TestCase
55 | {
56 | public function test()
57 | {
58 | $someObject = new stdClass();
59 |
60 | $this->assertInstanceof(stdClass::class, $someObject);
61 | }
62 | }
63 | CODE_SAMPLE
64 | ),
65 |
66 | ]
67 | );
68 | }
69 |
70 | /**
71 | * @return array>
72 | */
73 | public function getNodeTypes(): array
74 | {
75 | return [MethodCall::class];
76 | }
77 |
78 | /**
79 | * @param MethodCall $node
80 | */
81 | public function refactor(Node $node): ?Node
82 | {
83 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
84 | return null;
85 | }
86 |
87 | if (! $this->isNames($node->name, ['assertNotEmpty', 'assertEmpty', 'assertNull', 'assertNotNull'])) {
88 | return null;
89 | }
90 |
91 | if ($node->isFirstClassCallable()) {
92 | return null;
93 | }
94 |
95 | $firstArg = $node->getArgs()[0] ?? null;
96 | if (! $firstArg instanceof Arg) {
97 | return null;
98 | }
99 |
100 | $firstArgType = $this->getType($firstArg->value);
101 | if (! $firstArgType instanceof UnionType) {
102 | return null;
103 | }
104 |
105 | $pureType = TypeCombinator::removeNull($firstArgType);
106 | if (! $pureType instanceof ObjectType) {
107 | return null;
108 | }
109 |
110 | $methodName = $this->isNames(
111 | $node->name,
112 | ['assertEmpty', 'assertNull']
113 | ) ? 'assertNotInstanceOf' : 'assertInstanceOf';
114 |
115 | $node->name = new Identifier($methodName);
116 |
117 | $fullyQualified = new FullyQualified($pureType->getClassName());
118 |
119 | $customMessageArg = $node->getArgs()[1] ?? null;
120 |
121 | $node->args[0] = new Arg(new ClassConstFetch($fullyQualified, 'class'));
122 | $node->args[1] = $firstArg;
123 |
124 | if ($customMessageArg instanceof Arg) {
125 | $node->args[] = $customMessageArg;
126 | }
127 |
128 | return $node;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertEqualsOrAssertSameFloatParameterToSpecificMethodsTypeRector.php:
--------------------------------------------------------------------------------
1 | assertSame(10.20, $value);
41 | $this->assertEquals(10.200, $value);
42 | CODE_SAMPLE
43 | ,
44 | <<<'CODE_SAMPLE'
45 | $this->assertEqualsWithDelta(10.20, $value, PHP_FLOAT_EPSILON);
46 | $this->assertEqualsWithDelta(10.200, $value, PHP_FLOAT_EPSILON);
47 | CODE_SAMPLE
48 | ),
49 | ]
50 | );
51 | }
52 |
53 | /**
54 | * @return array>
55 | */
56 | public function getNodeTypes(): array
57 | {
58 | return [MethodCall::class];
59 | }
60 |
61 | /**
62 | * @param MethodCall $node
63 | */
64 | public function refactor(Node $node): ?Node
65 | {
66 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertEquals', 'assertSame'])) {
67 | return null;
68 | }
69 |
70 | if ($node->isFirstClassCallable()) {
71 | return null;
72 | }
73 |
74 | $args = $node->getArgs();
75 |
76 | $firstValue = $args[0]->value;
77 | if (! $firstValue instanceof Float_) {
78 | return null;
79 | }
80 |
81 | $customMessageArg = $args[2] ?? null;
82 |
83 | $newMethodCall = $this->assertCallFactory->createCallWithName($node, 'assertEqualsWithDelta');
84 | $newMethodCall->args[0] = $args[0];
85 | $newMethodCall->args[1] = $args[1];
86 | $newMethodCall->args[2] = new Arg(new ConstFetch(new Name('PHP_FLOAT_EPSILON')));
87 |
88 | if ($customMessageArg instanceof Arg) {
89 | $newMethodCall->args[] = $customMessageArg;
90 | }
91 |
92 | return $newMethodCall;
93 | }
94 |
95 | public function provideMinPhpVersion(): int
96 | {
97 | return PhpVersion::PHP_72;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertFalseStrposToContainsRector.php:
--------------------------------------------------------------------------------
1 |
24 | */
25 | private const RENAME_METHODS_MAP = [
26 | 'assertFalse' => 'assertStringNotContainsString',
27 | 'assertNotFalse' => 'assertStringContainsString',
28 | ];
29 |
30 | public function __construct(
31 | private readonly IdentifierManipulator $identifierManipulator,
32 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
33 | ) {
34 | }
35 |
36 | public function getRuleDefinition(): RuleDefinition
37 | {
38 | return new RuleDefinition(
39 | 'Turns `strpos`/`stripos` comparisons to their method name alternatives in PHPUnit TestCase',
40 | [
41 | new CodeSample(
42 | '$this->assertFalse(strpos($anything, "foo"), "message");',
43 | '$this->assertNotContains("foo", $anything, "message");'
44 | )]
45 | );
46 | }
47 |
48 | /**
49 | * @return array>
50 | */
51 | public function getNodeTypes(): array
52 | {
53 | return [MethodCall::class, StaticCall::class];
54 | }
55 |
56 | /**
57 | * @param MethodCall|StaticCall $node
58 | */
59 | public function refactor(Node $node): ?Node
60 | {
61 | $oldMethodName = array_keys(self::RENAME_METHODS_MAP);
62 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, $oldMethodName)) {
63 | return null;
64 | }
65 |
66 | if ($node->isFirstClassCallable()) {
67 | return null;
68 | }
69 |
70 | $firstArgumentValue = $node->getArgs()[0]
71 | ->value;
72 | if ($firstArgumentValue instanceof StaticCall) {
73 | return null;
74 | }
75 |
76 | if ($firstArgumentValue instanceof MethodCall) {
77 | return null;
78 | }
79 |
80 | if (! $this->isNames($firstArgumentValue, ['strpos', 'stripos'])) {
81 | return null;
82 | }
83 |
84 | $this->identifierManipulator->renameNodeWithMap($node, self::RENAME_METHODS_MAP);
85 |
86 | return $this->changeArgumentsOrder($node);
87 | }
88 |
89 | private function changeArgumentsOrder(MethodCall|StaticCall $node): MethodCall|StaticCall|null
90 | {
91 | $oldArguments = $node->getArgs();
92 |
93 | $strposFuncCallNode = $oldArguments[0]->value;
94 | if (! $strposFuncCallNode instanceof FuncCall) {
95 | return null;
96 | }
97 |
98 | $firstArgument = $strposFuncCallNode->getArgs()[1];
99 | $secondArgument = $strposFuncCallNode->getArgs()[0];
100 |
101 | unset($oldArguments[0]);
102 |
103 | $newArgs = [$firstArgument, $secondArgument];
104 | $node->args = [...$newArgs, ...$oldArguments];
105 |
106 | return $node;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertIssetToSpecificMethodRector.php:
--------------------------------------------------------------------------------
1 | assertTrue(isset($anything["foo"]), "message");',
37 | '$this->assertArrayHasKey("foo", $anything, "message");'
38 | ),
39 | ]
40 | );
41 | }
42 |
43 | /**
44 | * @return array>
45 | */
46 | public function getNodeTypes(): array
47 | {
48 | return [MethodCall::class, StaticCall::class];
49 | }
50 |
51 | /**
52 | * @param MethodCall|StaticCall $node
53 | */
54 | public function refactor(Node $node): ?Node
55 | {
56 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames(
57 | $node,
58 | [AssertMethod::ASSERT_TRUE, AssertMethod::ASSERT_FALSE]
59 | )) {
60 | return null;
61 | }
62 |
63 | if ($node->isFirstClassCallable()) {
64 | return null;
65 | }
66 |
67 | $firstArg = $node->getArgs()[0];
68 | $firstArgumentValue = $firstArg->value;
69 |
70 | // is property access
71 | if (! $firstArgumentValue instanceof Isset_) {
72 | return null;
73 | }
74 |
75 | $issetExpr = $firstArgumentValue->vars[0];
76 | if (! $issetExpr instanceof ArrayDimFetch) {
77 | return null;
78 | }
79 |
80 | return $this->refactorArrayDimFetchNode($node, $issetExpr);
81 | }
82 |
83 | private function refactorArrayDimFetchNode(MethodCall|StaticCall $node, ArrayDimFetch $arrayDimFetch): Node
84 | {
85 | $this->identifierManipulator->renameNodeWithMap($node, [
86 | AssertMethod::ASSERT_TRUE => 'assertArrayHasKey',
87 | AssertMethod::ASSERT_FALSE => 'assertArrayNotHasKey',
88 | ]);
89 |
90 | $oldArgs = $node->getArgs();
91 | unset($oldArgs[0]);
92 |
93 | $node->args = [...$this->nodeFactory->createArgs([$arrayDimFetch->dim, $arrayDimFetch->var]), ...$oldArgs];
94 | return $node;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertNotOperatorRector.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | private const RENAME_METHODS_MAP = [
27 | 'assertTrue' => 'assertFalse',
28 | 'assertFalse' => 'assertTrue',
29 | ];
30 |
31 | public function __construct(
32 | private readonly IdentifierManipulator $identifierManipulator,
33 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
34 | ) {
35 | }
36 |
37 | public function getRuleDefinition(): RuleDefinition
38 | {
39 | return new RuleDefinition(
40 | 'Turns not-operator comparisons to their method name alternatives in PHPUnit TestCase',
41 | [
42 | new CodeSample('$this->assertTrue(!$foo, "message");', '$this->assertFalse($foo, "message");'),
43 | new CodeSample('$this->assertFalse(!$foo, "message");', '$this->assertTrue($foo, "message");'),
44 | ]
45 | );
46 | }
47 |
48 | /**
49 | * @return array>
50 | */
51 | public function getNodeTypes(): array
52 | {
53 | return [MethodCall::class, StaticCall::class];
54 | }
55 |
56 | /**
57 | * @param MethodCall|StaticCall $node
58 | */
59 | public function refactor(Node $node): ?Node
60 | {
61 | $oldMethodNames = array_keys(self::RENAME_METHODS_MAP);
62 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, $oldMethodNames)) {
63 | return null;
64 | }
65 |
66 | if ($node->isFirstClassCallable()) {
67 | return null;
68 | }
69 |
70 | $firstArgumentValue = $node->getArgs()[0]
71 | ->value;
72 | if (! $firstArgumentValue instanceof BooleanNot) {
73 | return null;
74 | }
75 |
76 | $this->identifierManipulator->renameNodeWithMap($node, self::RENAME_METHODS_MAP);
77 |
78 | $oldArguments = $node->getArgs();
79 |
80 | /** @var BooleanNot $negation */
81 | $negation = $oldArguments[0]->value;
82 |
83 | $expression = $negation->expr;
84 |
85 | unset($oldArguments[0]);
86 |
87 | $node->args = array_merge([new Arg($expression)], $oldArguments);
88 |
89 | return $node;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertPropertyExistsRector.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | private const RENAME_METHODS_WITH_OBJECT_MAP = [
29 | 'assertTrue' => 'assertObjectHasAttribute',
30 | 'assertFalse' => 'assertObjectNotHasAttribute',
31 | ];
32 |
33 | /**
34 | * @var array
35 | */
36 | private const RENAME_METHODS_WITH_CLASS_MAP = [
37 | 'assertTrue' => 'assertClassHasAttribute',
38 | 'assertFalse' => 'assertClassNotHasAttribute',
39 | ];
40 |
41 | public function __construct(
42 | private readonly IdentifierManipulator $identifierManipulator,
43 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
44 | ) {
45 | }
46 |
47 | public function getRuleDefinition(): RuleDefinition
48 | {
49 | return new RuleDefinition(
50 | 'Turns `property_exists` comparisons to their method name alternatives in PHPUnit TestCase',
51 | [
52 | new CodeSample(
53 | <<<'CODE_SAMPLE'
54 | $this->assertFalse(property_exists(new Class, "property"));
55 | $this->assertTrue(property_exists(new Class, "property"));
56 | CODE_SAMPLE
57 | ,
58 | <<<'CODE_SAMPLE'
59 | $this->assertClassHasAttribute("property", "Class");
60 | $this->assertClassNotHasAttribute("property", "Class");
61 | CODE_SAMPLE
62 | ),
63 | ]
64 | );
65 | }
66 |
67 | /**
68 | * @return array>
69 | */
70 | public function getNodeTypes(): array
71 | {
72 | return [MethodCall::class, StaticCall::class];
73 | }
74 |
75 | /**
76 | * @param MethodCall|StaticCall $node
77 | */
78 | public function refactor(Node $node): ?Node
79 | {
80 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertTrue', 'assertFalse'])) {
81 | return null;
82 | }
83 |
84 | if ($node->isFirstClassCallable()) {
85 | return null;
86 | }
87 |
88 | $firstArgumentValue = $node->getArgs()[0]
89 | ->value;
90 | if (! $firstArgumentValue instanceof FuncCall) {
91 | return null;
92 | }
93 |
94 | if (! $this->isName($firstArgumentValue, 'property_exists')) {
95 | return null;
96 | }
97 |
98 | $propertyExistsMethodCall = $node->getArgs()[0]
99 | ->value;
100 | if (! $propertyExistsMethodCall instanceof FuncCall) {
101 | return null;
102 | }
103 |
104 | $firstArgument = $propertyExistsMethodCall->getArgs()[0];
105 | $secondArgument = $propertyExistsMethodCall->getArgs()[1];
106 |
107 | if ($firstArgument->value instanceof Variable) {
108 | $secondArg = new Variable($firstArgument->value->name);
109 | $map = self::RENAME_METHODS_WITH_OBJECT_MAP;
110 | } elseif ($firstArgument->value instanceof New_) {
111 | $secondArg = $this->getName($firstArgument->value->class);
112 | $map = self::RENAME_METHODS_WITH_CLASS_MAP;
113 | } else {
114 | return null;
115 | }
116 |
117 | if (! $secondArgument->value instanceof String_) {
118 | return null;
119 | }
120 |
121 | unset($node->args[0]);
122 |
123 | $newArgs = $this->nodeFactory->createArgs([$secondArgument->value->value, $secondArg]);
124 | $node->args = array_merge($newArgs, $node->getArgs());
125 |
126 | $this->identifierManipulator->renameNodeWithMap($node, $map);
127 |
128 | return $node;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertSameBoolNullToSpecificMethodRector.php:
--------------------------------------------------------------------------------
1 | constantWithAssertMethods = [
35 | new ConstantWithAssertMethods('null', 'assertNull', 'assertNotNull'),
36 | new ConstantWithAssertMethods('true', 'assertTrue', 'assertNotTrue'),
37 | new ConstantWithAssertMethods('false', 'assertFalse', 'assertNotFalse'),
38 | ];
39 | }
40 |
41 | public function getRuleDefinition(): RuleDefinition
42 | {
43 | return new RuleDefinition(
44 | 'Turns same bool and null comparisons to their method name alternatives in PHPUnit TestCase',
45 | [
46 | new CodeSample('$this->assertSame(null, $anything);', '$this->assertNull($anything);'),
47 | new CodeSample('$this->assertNotSame(false, $anything);', '$this->assertNotFalse($anything);'),
48 | ]
49 | );
50 | }
51 |
52 | /**
53 | * @return array>
54 | */
55 | public function getNodeTypes(): array
56 | {
57 | return [MethodCall::class, StaticCall::class];
58 | }
59 |
60 | /**
61 | * @param MethodCall|StaticCall $node
62 | */
63 | public function refactor(Node $node): ?Node
64 | {
65 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertSame', 'assertNotSame'])) {
66 | return null;
67 | }
68 |
69 | if ($node->isFirstClassCallable()) {
70 | return null;
71 | }
72 |
73 | $firstArgumentValue = $node->getArgs()[0]
74 | ->value;
75 | if (! $firstArgumentValue instanceof ConstFetch) {
76 | return null;
77 | }
78 |
79 | foreach ($this->constantWithAssertMethods as $constantWithAssertMethod) {
80 | if (! $this->isName($firstArgumentValue, $constantWithAssertMethod->getConstant())) {
81 | continue;
82 | }
83 |
84 | $this->renameMethod($node, $constantWithAssertMethod);
85 | $this->argumentMover->removeFirstArg($node);
86 |
87 | return $node;
88 | }
89 |
90 | return null;
91 | }
92 |
93 | private function renameMethod(
94 | MethodCall|StaticCall $node,
95 | ConstantWithAssertMethods $constantWithAssertMethods
96 | ): void {
97 | $this->identifierManipulator->renameNodeWithMap($node, [
98 | 'assertSame' => $constantWithAssertMethods->getAssetMethodName(),
99 | 'assertNotSame' => $constantWithAssertMethods->getNotAssertMethodName(),
100 | ]);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/AssertSameTrueFalseToAssertTrueFalseRector.php:
--------------------------------------------------------------------------------
1 | assertSame(true, ...) to assertTrue()',
33 | [
34 | new CodeSample(
35 | <<<'CODE_SAMPLE'
36 | use PHPUnit\Framework\TestCase;
37 |
38 | final class SomeTest extends TestCase
39 | {
40 | public function test()
41 | {
42 | $value = (bool) mt_rand(0, 1);
43 | $this->assertSame(true, $value);
44 | }
45 | }
46 | CODE_SAMPLE
47 |
48 | ,
49 | <<<'CODE_SAMPLE'
50 | use PHPUnit\Framework\TestCase;
51 |
52 | final class SomeTest extends TestCase
53 | {
54 | public function test()
55 | {
56 | $value = (bool) mt_rand(0, 1);
57 | $this->assertTrue($value);
58 | }
59 | }
60 | CODE_SAMPLE
61 | ),
62 | ]
63 | );
64 | }
65 |
66 | /**
67 | * @return array>
68 | */
69 | public function getNodeTypes(): array
70 | {
71 | return [MethodCall::class];
72 | }
73 |
74 | /**
75 | * @param MethodCall $node
76 | */
77 | public function refactor(Node $node): ?Node
78 | {
79 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames(
80 | $node,
81 | ['assertSame', 'assertEqual', 'assertNotSame', 'assertNotEqual']
82 | )) {
83 | return null;
84 | }
85 |
86 | if ($node->isFirstClassCallable()) {
87 | return null;
88 | }
89 |
90 | $firstArg = $node->getArgs()[0];
91 |
92 | if ($this->valueResolver->isTrue($firstArg->value)) {
93 | $this->argumentMover->removeFirstArg($node);
94 |
95 | $node->name = new Identifier('assertTrue');
96 |
97 | return $node;
98 | }
99 |
100 | if ($this->valueResolver->isFalse($firstArg->value)) {
101 | $this->argumentMover->removeFirstArg($node);
102 |
103 | $node->name = new Identifier('assertFalse');
104 | return $node;
105 | }
106 |
107 | return null;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/FlipAssertRector.php:
--------------------------------------------------------------------------------
1 | assertSame($result, 'expected');
53 | }
54 | }
55 | CODE_SAMPLE
56 | ,
57 | <<<'CODE_SAMPLE'
58 | assertSame('expected', $result);
68 | }
69 | }
70 | CODE_SAMPLE
71 | ),
72 | ]
73 | );
74 | }
75 |
76 | /**
77 | * @return array>
78 | */
79 | public function getNodeTypes(): array
80 | {
81 | return [MethodCall::class, StaticCall::class];
82 | }
83 |
84 | /**
85 | * @param MethodCall|StaticCall $node
86 | */
87 | public function refactor(Node $node): ?Node
88 | {
89 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, self::METHOD_NAMES)) {
90 | return null;
91 | }
92 |
93 | if ($node->isFirstClassCallable()) {
94 | return null;
95 | }
96 |
97 | $firstArg = $node->getArgs()[0];
98 | $secondArg = $node->getArgs()[1];
99 |
100 | // correct location
101 | if ($this->isScalarValue($firstArg->value)) {
102 | return null;
103 | }
104 |
105 | if (! $this->isScalarValue($secondArg->value)) {
106 | return null;
107 | }
108 |
109 | $oldArgs = $node->getArgs();
110 | // flip args
111 | [$oldArgs[0], $oldArgs[1]] = [$oldArgs[1], $oldArgs[0]];
112 |
113 | $node->args = $oldArgs;
114 | return $node;
115 | }
116 |
117 | private function isScalarValue(Expr $expr): bool
118 | {
119 | if ($expr instanceof Scalar) {
120 | return true;
121 | }
122 |
123 | if ($expr instanceof ConstFetch) {
124 | return true;
125 | }
126 |
127 | return $expr instanceof ClassConstFetch;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/NarrowIdenticalWithConsecutiveRector.php:
--------------------------------------------------------------------------------
1 | personServiceMock->expects($this->exactly(3))
41 | ->method('prepare')
42 | ->withConsecutive(
43 | [1],
44 | [1],
45 | [1],
46 | )
47 | ->willReturnOnConsecutiveCalls(
48 | [2],
49 | [2],
50 | [2],
51 | );
52 | }
53 | }
54 | CODE_SAMPLE
55 | ,
56 | <<<'CODE_SAMPLE'
57 | use PHPUnit\Framework\TestCase;
58 |
59 | final class SomeTest extends TestCase
60 | {
61 | public function run()
62 | {
63 | $this->personServiceMock->expects($this->exactly(3))
64 | ->method('prepare')
65 | ->with([1])
66 | ->willReturn([2]);
67 | }
68 | }
69 | CODE_SAMPLE
70 | ),
71 | ]
72 | );
73 | }
74 |
75 | /**
76 | * @return array>
77 | */
78 | public function getNodeTypes(): array
79 | {
80 | return [MethodCall::class];
81 | }
82 |
83 | /**
84 | * @param MethodCall $node
85 | */
86 | public function refactor(Node $node): MethodCall|null
87 | {
88 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
89 | return null;
90 | }
91 |
92 | if (! $this->isNames($node->name, ['withConsecutive', 'willReturnOnConsecutiveCalls'])) {
93 | return null;
94 | }
95 |
96 | if ($node->isFirstClassCallable()) {
97 | return null;
98 | }
99 |
100 | $firstArg = $node->getArgs()[0];
101 |
102 | // skip as most likely nested array of unique values
103 | if ($firstArg->unpack) {
104 | return null;
105 | }
106 |
107 | $uniqueArgValues = $this->resolveUniqueArgValues($node);
108 |
109 | // multiple unique values
110 | if (count($uniqueArgValues) !== 1) {
111 | return null;
112 | }
113 |
114 | $firstArg = $node->getArgs()[0];
115 |
116 | if ($this->isName($node->name, 'withConsecutive')) {
117 | $node->name = new Identifier('with');
118 | } else {
119 | $node->name = new Identifier('willReturn');
120 | }
121 |
122 | // use simpler with() instead
123 | $node->args = [new Arg($firstArg->value)];
124 |
125 | return $node;
126 | }
127 |
128 | /**
129 | * @return string[]
130 | */
131 | private function resolveUniqueArgValues(MethodCall $methodCall): array
132 | {
133 | $printerStandard = new Standard();
134 | $printedValues = [];
135 |
136 | foreach ($methodCall->getArgs() as $arg) {
137 | $printedValues[] = $printerStandard->prettyPrintExpr($arg->value);
138 | }
139 |
140 | return array_unique($printedValues);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/RemoveExpectAnyFromMockRector.php:
--------------------------------------------------------------------------------
1 | any())` from mocks as it has no added value',
30 | [
31 | new CodeSample(
32 | <<<'CODE_SAMPLE'
33 | use PHPUnit\Framework\TestCase;
34 |
35 | class SomeClass extends TestCase
36 | {
37 | public function test()
38 | {
39 | $translator = $this->getMock('SomeClass');
40 | $translator->expects($this->any())
41 | ->method('trans')
42 | ->willReturn('translated max {{ max }}!');
43 | }
44 | }
45 | CODE_SAMPLE
46 | ,
47 | <<<'CODE_SAMPLE'
48 | use PHPUnit\Framework\TestCase;
49 |
50 | class SomeClass extends TestCase
51 | {
52 | public function test()
53 | {
54 | $translator = $this->getMock('SomeClass');
55 | $translator->method('trans')
56 | ->willReturn('translated max {{ max }}!');
57 | }
58 | }
59 | CODE_SAMPLE
60 | ),
61 | ]
62 | );
63 | }
64 |
65 | /**
66 | * @return array>
67 | */
68 | public function getNodeTypes(): array
69 | {
70 | return [MethodCall::class];
71 | }
72 |
73 | /**
74 | * @param MethodCall $node
75 | */
76 | public function refactor(Node $node): ?Node
77 | {
78 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
79 | return null;
80 | }
81 |
82 | if (! $this->isName($node->name, 'expects')) {
83 | return null;
84 | }
85 |
86 | if ($node->isFirstClassCallable()) {
87 | return null;
88 | }
89 |
90 | if (count($node->args) !== 1) {
91 | return null;
92 | }
93 |
94 | $onlyArgument = $node->getArgs()[0]
95 | ->value;
96 | if (! $this->isMethodCallOnVariableNamed($onlyArgument, 'this', 'any')) {
97 | return null;
98 | }
99 |
100 | return $node->var;
101 | }
102 |
103 | private function isMethodCallOnVariableNamed(Expr $expr, string $variableName, string $methodName): bool
104 | {
105 | if (! $expr instanceof MethodCall) {
106 | return false;
107 | }
108 |
109 | if (! $this->isName($expr->var, $variableName)) {
110 | return false;
111 | }
112 |
113 | return $this->isName($expr->name, $methodName);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/SingleWithConsecutiveToWithRector.php:
--------------------------------------------------------------------------------
1 | personServiceMock->expects($this->exactly(3))
43 | ->method('prepare')
44 | ->withConsecutive(
45 | [1],
46 | );
47 | }
48 | }
49 | CODE_SAMPLE
50 | ,
51 | <<<'CODE_SAMPLE'
52 | use PHPUnit\Framework\TestCase;
53 |
54 | final class SomeTest extends TestCase
55 | {
56 | public function run()
57 | {
58 | $this->personServiceMock->expects($this->exactly(3))
59 | ->method('prepare')
60 | ->with([1]);
61 | }
62 | }
63 | CODE_SAMPLE
64 | ),
65 | ]
66 | );
67 | }
68 |
69 | /**
70 | * @return array>
71 | */
72 | public function getNodeTypes(): array
73 | {
74 | return [MethodCall::class];
75 | }
76 |
77 | /**
78 | * @param MethodCall $node
79 | */
80 | public function refactor(Node $node): MethodCall|null
81 | {
82 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
83 | return null;
84 | }
85 |
86 | if (! $this->isNames($node->name, ['withConsecutive', 'willReturnOnConsecutiveCalls'])) {
87 | return null;
88 | }
89 |
90 | if ($node->isFirstClassCallable()) {
91 | return null;
92 | }
93 |
94 | if (count($node->getArgs()) !== 1) {
95 | return null;
96 | }
97 |
98 | $firstArg = $node->getArgs()[0];
99 |
100 | // skip as multiple unique values
101 | if ($firstArg->unpack) {
102 | return null;
103 | }
104 |
105 | // use simpler with()/willReturn() instead
106 | if ($this->isName($node->name, 'withConsecutive')) {
107 | $node->name = new Identifier('with');
108 | } else {
109 | $node->name = new Identifier('willReturn');
110 | }
111 |
112 | // has assert inside?
113 | $hasAssertInside = (bool) $this->betterNodeFinder->findFirst(
114 | $firstArg->value,
115 | function (Node $node): bool {
116 | if (! $node instanceof MethodCall) {
117 | return false;
118 | }
119 |
120 | return $this->isNames($node->name, ['equalTo', 'instanceOf']);
121 | }
122 | );
123 |
124 | // replace $this->equalsTo() with direct value
125 | $this->traverseNodesWithCallable($firstArg->value, function (Node $node): ?Node {
126 | if (! $node instanceof MethodCall) {
127 | return null;
128 | }
129 |
130 | if (! $this->isName($node->name, 'equalTo')) {
131 | return null;
132 | }
133 |
134 | return $node->getArgs()[0]
135 | ->value;
136 | });
137 |
138 | if ($hasAssertInside && $firstArg->value instanceof Array_) {
139 | $args = $this->nodeFactory->createArgs($firstArg->value->items);
140 | } else {
141 | $args = [new Arg($firstArg->value)];
142 | }
143 |
144 | $node->args = $args;
145 |
146 | return $node;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Rector/MethodCall/UseSpecificWithMethodRector.php:
--------------------------------------------------------------------------------
1 | with() to more specific method',
31 | [
32 | new CodeSample(
33 | <<<'CODE_SAMPLE'
34 | class SomeClass extends PHPUnit\Framework\TestCase
35 | {
36 | public function test()
37 | {
38 | $translator = $this->createMock('SomeClass');
39 |
40 | $translator->expects($this->any())
41 | ->method('trans')
42 | ->with($this->equalTo('old max {{ max }}!'));
43 | }
44 | }
45 | CODE_SAMPLE
46 | ,
47 | <<<'CODE_SAMPLE'
48 | class SomeClass extends PHPUnit\Framework\TestCase
49 | {
50 | public function test()
51 | {
52 | $translator = $this->createMock('SomeClass');
53 |
54 | $translator->expects($this->any())
55 | ->method('trans')
56 | ->with('old max {{ max }}!');
57 | }
58 | }
59 | CODE_SAMPLE
60 | ),
61 | ]
62 | );
63 | }
64 |
65 | /**
66 | * @return array>
67 | */
68 | public function getNodeTypes(): array
69 | {
70 | return [MethodCall::class, StaticCall::class];
71 | }
72 |
73 | /**
74 | * @param MethodCall|StaticCall $node
75 | */
76 | public function refactor(Node $node): ?Node
77 | {
78 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
79 | return null;
80 | }
81 |
82 | // we cannot check caller types, as on old PHPUnit version, this the magic ->method() call result to a mixed type
83 | if (! $this->isName($node->name, 'with')) {
84 | return null;
85 | }
86 |
87 | if ($node->isFirstClassCallable()) {
88 | return null;
89 | }
90 |
91 | foreach ($node->getArgs() as $i => $argNode) {
92 | if (! $argNode->value instanceof MethodCall) {
93 | continue;
94 | }
95 |
96 | $methodCall = $argNode->value;
97 | if (! $this->isName($methodCall->name, 'equalTo')) {
98 | continue;
99 | }
100 |
101 | $node->args[$i] = $methodCall->getArgs()[0];
102 | }
103 |
104 | return $node;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/rules/CodeQuality/Reflection/MethodParametersAndReturnTypesResolver.php:
--------------------------------------------------------------------------------
1 | getTypes() as $intersectionedType) {
23 | if (! $intersectionedType instanceof ObjectType) {
24 | continue;
25 | }
26 |
27 | if ($intersectionedType->getClassName() === ClassName::MOCK_OBJECT) {
28 | continue;
29 | }
30 |
31 | $classReflection = $intersectionedType->getClassReflection();
32 | if (! $classReflection instanceof ClassReflection) {
33 | continue;
34 | }
35 |
36 | if (! $classReflection->hasNativeMethod($methodName)) {
37 | continue;
38 | }
39 |
40 | $mockedMethodReflection = $classReflection->getNativeMethod($methodName);
41 |
42 | $parameterTypes = $this->resolveParameterTypes($mockedMethodReflection);
43 | $returnType = $this->resolveReturnType($mockedMethodReflection);
44 |
45 | return new ParamTypesAndReturnType($parameterTypes, $returnType);
46 | }
47 |
48 | return null;
49 | }
50 |
51 | /**
52 | * @return Type[]
53 | */
54 | private function resolveParameterTypes(ExtendedMethodReflection $extendedMethodReflection): array
55 | {
56 | $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors(
57 | $extendedMethodReflection->getVariants()
58 | );
59 |
60 | $parameterTypes = [];
61 | foreach ($extendedParametersAcceptor->getParameters() as $parameterReflection) {
62 | $parameterTypes[] = $parameterReflection->getType();
63 | }
64 |
65 | return $parameterTypes;
66 | }
67 |
68 | private function resolveReturnType(ExtendedMethodReflection $extendedMethodReflection): Type
69 | {
70 | $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors(
71 | $extendedMethodReflection->getVariants()
72 | );
73 |
74 | return $extendedParametersAcceptor->getReturnType();
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/rules/CodeQuality/ValueObject/MatchAndReturnMatch.php:
--------------------------------------------------------------------------------
1 | consecutiveMatch;
23 | }
24 |
25 | public function getConsecutiveMatchExpr(): Expr
26 | {
27 | $soleArm = $this->consecutiveMatch->arms[0];
28 | if ($soleArm->body instanceof CallLike) {
29 | $assertCall = $soleArm->body;
30 | $firstArg = $assertCall->getArgs()[0];
31 | return $firstArg->value;
32 | }
33 |
34 | throw new ShouldNotHappenException();
35 | }
36 |
37 | public function getWillReturnMatch(): ?Match_
38 | {
39 | return $this->willReturnMatch;
40 | }
41 |
42 | public function getWillReturnMatchExpr(): Expr
43 | {
44 | if (! $this->willReturnMatch instanceof Match_) {
45 | throw new ShouldNotHappenException();
46 | }
47 |
48 | $soleArm = $this->willReturnMatch->arms[0];
49 | return $soleArm->body;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/rules/CodeQuality/ValueObject/ParamTypesAndReturnType.php:
--------------------------------------------------------------------------------
1 | paramTypes;
26 | }
27 |
28 | public function getReturnType(): ?Type
29 | {
30 | return $this->returnType;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/rules/CodeQuality/ValueObject/VariableNameToType.php:
--------------------------------------------------------------------------------
1 | variableName;
18 | }
19 |
20 | public function getObjectType(): string
21 | {
22 | return $this->objectType;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/rules/CodeQuality/ValueObject/VariableNameToTypeCollection.php:
--------------------------------------------------------------------------------
1 | variableNameToType as $variableNameToType) {
20 | if ($variableNameToType->getVariableName() !== $variableName) {
21 | continue;
22 | }
23 |
24 | return $variableNameToType;
25 | }
26 |
27 | return null;
28 | }
29 |
30 | public function remove(VariableNameToType $matchedNullableVariableNameToType): void
31 | {
32 | foreach ($this->variableNameToType as $key => $variableNamesToType) {
33 | if ($matchedNullableVariableNameToType !== $variableNamesToType) {
34 | continue;
35 | }
36 |
37 | unset($this->variableNameToType[$key]);
38 | break;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/NodeDecorator/WillReturnIfNodeDecorator.php:
--------------------------------------------------------------------------------
1 | stmts as $key => $stmt) {
21 | if (! $stmt instanceof If_) {
22 | continue;
23 | }
24 |
25 | $currentArg = $willReturnOnConsecutiveMethodCall->getArgs()[$key];
26 | $stmt->stmts[] = new Return_($currentArg->value);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/NodeFactory/WillReturnCallbackFactory.php:
--------------------------------------------------------------------------------
1 | usedVariablesResolver->resolveUsedVariables($withConsecutiveMethodCall, $returnStmt);
42 |
43 | $closureStmts = $this->createParametersMatch($withConsecutiveMethodCall);
44 | if ($returnStmt instanceof Stmt) {
45 | $closureStmts[] = $returnStmt;
46 | }
47 |
48 | $parametersParam = new Param(new Variable(ConsecutiveVariable::PARAMETERS));
49 | $parametersParam->variadic = true;
50 |
51 | return new Closure([
52 | 'byRef' => $this->isByRef($referenceVariable),
53 | 'uses' => $this->createClosureUses($matcherVariable, $usedVariables),
54 | 'params' => [$parametersParam],
55 | 'stmts' => $closureStmts,
56 | ]);
57 | }
58 |
59 | /**
60 | * @return Stmt[]
61 | */
62 | public function createParametersMatch(MethodCall $withConsecutiveMethodCall): array
63 | {
64 | $parametersVariable = new Variable(ConsecutiveVariable::PARAMETERS);
65 |
66 | $firstArg = $withConsecutiveMethodCall->getArgs()[0] ?? null;
67 | if ($firstArg instanceof Arg && $firstArg->unpack) {
68 | $assertSameMethodCall = $this->createAssertSameDimFetch($firstArg, $parametersVariable);
69 | return [new Expression($assertSameMethodCall)];
70 | }
71 |
72 | $numberOfInvocationsMethodCall = $this->matcherInvocationCountMethodCallNodeFactory->create();
73 |
74 | return $this->consecutiveIfsFactory->createIfs($withConsecutiveMethodCall, $numberOfInvocationsMethodCall);
75 | }
76 |
77 | private function createAssertSameDimFetch(Arg $firstArg, Variable $variable): MethodCall
78 | {
79 | $matcherCountMethodCall = $this->matcherInvocationCountMethodCallNodeFactory->create();
80 |
81 | $currentValueArrayDimFetch = new ArrayDimFetch($firstArg->value, new Minus(
82 | $matcherCountMethodCall,
83 | new Int_(1)
84 | ));
85 |
86 | $compareArgs = [new Arg($currentValueArrayDimFetch), new Arg($variable)];
87 |
88 | return $this->builderFactory->methodCall(new Variable('this'), 'assertSame', $compareArgs);
89 | }
90 |
91 | private function isByRef(Expr|Variable|null $referenceVariable): bool
92 | {
93 | return $referenceVariable instanceof Variable;
94 | }
95 |
96 | /**
97 | * @param Variable[] $usedVariables
98 | * @return ClosureUse[]
99 | */
100 | private function createClosureUses(Variable $matcherVariable, array $usedVariables): array
101 | {
102 | $uses = [new ClosureUse($matcherVariable)];
103 |
104 | foreach ($usedVariables as $usedVariable) {
105 | $uses[] = new ClosureUse($usedVariable);
106 | }
107 |
108 | return $uses;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/Class_/AddProphecyTraitRector.php:
--------------------------------------------------------------------------------
1 | prophesize()',
44 | [
45 | new CodeSample(
46 | <<<'CODE_SAMPLE'
47 | use PHPUnit\Framework\TestCase;
48 |
49 | final class ExampleTest extends TestCase
50 | {
51 | public function testOne(): void
52 | {
53 | $prophecy = $this->prophesize(\AnInterface::class);
54 | }
55 | }
56 | CODE_SAMPLE
57 | ,
58 | <<<'CODE_SAMPLE'
59 | use PHPUnit\Framework\TestCase;
60 | use Prophecy\PhpUnit\ProphecyTrait;
61 |
62 | final class ExampleTest extends TestCase
63 | {
64 | use ProphecyTrait;
65 |
66 | public function testOne(): void
67 | {
68 | $prophecy = $this->prophesize(\AnInterface::class);
69 | }
70 | }
71 | CODE_SAMPLE
72 | ),
73 | ]
74 | );
75 | }
76 |
77 | /**
78 | * @return array>
79 | */
80 | public function getNodeTypes(): array
81 | {
82 | return [Class_::class];
83 | }
84 |
85 | /**
86 | * @param Class_ $node
87 | */
88 | public function refactor(Node $node): ?Node
89 | {
90 | if ($this->shouldSkipClass($node)) {
91 | return null;
92 | }
93 |
94 | $traitUse = new TraitUse([new FullyQualified(self::PROPHECY_TRAIT)]);
95 |
96 | $node->stmts = array_merge([$traitUse], $node->stmts);
97 |
98 | return $node;
99 | }
100 |
101 | private function shouldSkipClass(Class_ $class): bool
102 | {
103 | $hasProphesizeMethodCall = (bool) $this->betterNodeFinder->findFirst(
104 | $class,
105 | fn (Node $node): bool => $this->testsNodeAnalyzer->isAssertMethodCallName($node, 'prophesize')
106 | );
107 |
108 | if (! $hasProphesizeMethodCall) {
109 | return true;
110 | }
111 |
112 | $classReflection = $this->reflectionResolver->resolveClassReflection($class);
113 | if (! $classReflection instanceof ClassReflection) {
114 | return false;
115 | }
116 |
117 | return $classReflection->hasTraitUse(self::PROPHECY_TRAIT);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/Class_/ParentTestClassConstructorRector.php:
--------------------------------------------------------------------------------
1 | >
65 | */
66 | public function getNodeTypes(): array
67 | {
68 | return [Class_::class];
69 | }
70 |
71 | /**
72 | * @param Class_ $node
73 | */
74 | public function refactor(Node $node): ?Node
75 | {
76 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
77 | return null;
78 | }
79 |
80 | if ($this->shouldSkipClass($node)) {
81 | return null;
82 | }
83 |
84 | // it already has a constructor, skip as it might require specific tweaking
85 | if ($node->getMethod(MethodName::CONSTRUCT)) {
86 | return null;
87 | }
88 |
89 | $constructorClassMethod = new ClassMethod(MethodName::CONSTRUCT);
90 | $constructorClassMethod->flags |= Modifiers::PUBLIC;
91 | $constructorClassMethod->stmts[] = new Expression($this->createParentConstructorCall());
92 |
93 | $node->stmts = array_merge([$constructorClassMethod], $node->stmts);
94 |
95 | return $node;
96 | }
97 |
98 | private function createParentConstructorCall(): StaticCall
99 | {
100 | $staticClassConstFetch = new ClassConstFetch(new Name('static'), 'class');
101 |
102 | return new StaticCall(new Name('parent'), MethodName::CONSTRUCT, [new Arg($staticClassConstFetch)]);
103 | }
104 |
105 | private function shouldSkipClass(Class_ $class): bool
106 | {
107 | if ($class->isAbstract()) {
108 | return true;
109 | }
110 |
111 | if ($class->isAnonymous()) {
112 | return true;
113 | }
114 |
115 | $className = $this->getName($class);
116 |
117 | // loaded automatically by PHPUnit
118 | if (str_ends_with((string) $className, 'Test')) {
119 | return true;
120 | }
121 |
122 | if (str_ends_with((string) $className, 'TestCase')) {
123 | return true;
124 | }
125 |
126 | return (bool) $class->getAttribute('hasRemovedFinalConstruct');
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/Class_/PublicDataProviderClassMethodRector.php:
--------------------------------------------------------------------------------
1 | >
77 | */
78 | public function getNodeTypes(): array
79 | {
80 | return [Class_::class];
81 | }
82 |
83 | /**
84 | * @param Class_ $node
85 | */
86 | public function refactor(Node $node): ?Node
87 | {
88 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
89 | return null;
90 | }
91 |
92 | // 1. find all data providers
93 | $dataProviderClassMethods = $this->dataProviderClassMethodFinder->find($node);
94 |
95 | $hasChanged = false;
96 |
97 | foreach ($dataProviderClassMethods as $dataProviderClassMethod) {
98 | if ($this->skipMethod($dataProviderClassMethod)) {
99 | continue;
100 | }
101 |
102 | $this->visibilityManipulator->makePublic($dataProviderClassMethod);
103 | $hasChanged = true;
104 | }
105 |
106 | if ($hasChanged) {
107 | return $node;
108 | }
109 |
110 | return null;
111 | }
112 |
113 | private function skipMethod(ClassMethod $classMethod): bool
114 | {
115 | return $classMethod->isPublic();
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/Class_/RemoveNamedArgsInDataProviderRector.php:
--------------------------------------------------------------------------------
1 | 100];
52 | }
53 | }
54 | CODE_SAMPLE
55 |
56 | ,
57 | <<<'CODE_SAMPLE'
58 | use PHPUnit\Framework\TestCase;
59 |
60 | final class SomeTest extends TestCase
61 | {
62 | /**
63 | * @dataProvider provideData()
64 | */
65 | public function test()
66 | {
67 | }
68 |
69 | public static function provideData()
70 | {
71 | yield [100];
72 | }
73 | }
74 | CODE_SAMPLE
75 | ),
76 | ]);
77 | }
78 |
79 | /**
80 | * @return array>
81 | */
82 | public function getNodeTypes(): array
83 | {
84 | return [Class_::class];
85 | }
86 |
87 | /**
88 | * @param Class_ $node
89 | */
90 | public function refactor(Node $node): ?Node
91 | {
92 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
93 | return null;
94 | }
95 |
96 | $hasChanged = false;
97 |
98 | $dataProviders = $this->dataProviderClassMethodFinder->find($node);
99 | foreach ($dataProviders as $dataProvider) {
100 | /** @var Expression $stmt */
101 | foreach ($dataProvider->getStmts() ?? [] as $stmt) {
102 | $expr = $stmt->expr;
103 | $arrayChanged = false;
104 | if ($expr instanceof Yield_) {
105 | $arrayChanged = $this->handleArray($expr->value);
106 | } elseif ($expr instanceof Array_) {
107 | $arrayChanged = $this->handleArray($expr);
108 | }
109 |
110 | if ($arrayChanged) {
111 | $hasChanged = true;
112 | }
113 | }
114 | }
115 |
116 | if ($hasChanged) {
117 | return $node;
118 | }
119 |
120 | return null;
121 | }
122 |
123 | private function handleArray(Array_ $array): bool
124 | {
125 | $hasChanged = false;
126 | foreach ($array->items as $item) {
127 | if (! $item instanceof ArrayItem) {
128 | continue;
129 | }
130 |
131 | if (! $item->key instanceof Expr) {
132 | continue;
133 | }
134 |
135 | if (! $item->key instanceof Int_ && $item->key instanceof Expr) {
136 | $item->key = null;
137 | $hasChanged = true;
138 | }
139 | }
140 |
141 | return $hasChanged;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/Class_/StaticDataProviderClassMethodRector.php:
--------------------------------------------------------------------------------
1 | >
80 | */
81 | public function getNodeTypes(): array
82 | {
83 | return [Class_::class];
84 | }
85 |
86 | /**
87 | * @param Class_ $node
88 | */
89 | public function refactor(Node $node): ?Node
90 | {
91 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
92 | return null;
93 | }
94 |
95 | // 1. find all data providers
96 | $dataProviderClassMethods = $this->dataProviderClassMethodFinder->find($node);
97 |
98 | $hasChanged = false;
99 |
100 | foreach ($dataProviderClassMethods as $dataProviderClassMethod) {
101 | if ($this->skipMethod($dataProviderClassMethod)) {
102 | continue;
103 | }
104 |
105 | $this->visibilityManipulator->makeStatic($dataProviderClassMethod);
106 | $hasChanged = true;
107 | }
108 |
109 | if ($hasChanged) {
110 | return $node;
111 | }
112 |
113 | return null;
114 | }
115 |
116 | private function skipMethod(ClassMethod $classMethod): bool
117 | {
118 | if ($classMethod->isStatic()) {
119 | return true;
120 | }
121 |
122 | if ($classMethod->stmts === null) {
123 | return false;
124 | }
125 |
126 | return (bool) $this->betterNodeFinder->findFirst(
127 | $classMethod->stmts,
128 | fn (Node $node): bool => $node instanceof Variable && $this->isName($node, 'this')
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/MethodCall/PropertyExistsWithoutAssertRector.php:
--------------------------------------------------------------------------------
1 |
28 | */
29 | private const RENAME_METHODS_WITH_OBJECT_MAP = [
30 | 'assertClassHasStaticAttribute' => 'assertTrue',
31 | 'classHasStaticAttribute' => 'assertTrue',
32 | 'assertClassNotHasStaticAttribute' => 'assertFalse',
33 | ];
34 |
35 | public function __construct(
36 | private readonly IdentifierManipulator $identifierManipulator,
37 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
38 | ) {
39 | }
40 |
41 | public function getRuleDefinition(): RuleDefinition
42 | {
43 | return new RuleDefinition(
44 | 'Replace deleted PHPUnit methods: assertClassHasStaticAttribute, classHasStaticAttribute and assertClassNotHasStaticAttribute by property_exists()',
45 | [
46 | new CodeSample(
47 | <<<'CODE_SAMPLE'
48 | $this->assertClassHasStaticAttribute("Class", "property");
49 | $this->classHasStaticAttribute("Class", "property");
50 | $this->assertClassNotHasStaticAttribute("Class", "property");
51 | CODE_SAMPLE
52 | ,
53 | <<<'CODE_SAMPLE'
54 | $this->assertTrue(property_exists("Class", "property"));
55 | $this->assertTrue(property_exists("Class", "property"));
56 | $this->assertFalse(property_exists("Class", "property"));
57 | CODE_SAMPLE
58 | ),
59 | ]
60 | );
61 | }
62 |
63 | /**
64 | * @return array>
65 | */
66 | public function getNodeTypes(): array
67 | {
68 | return [MethodCall::class];
69 | }
70 |
71 | /**
72 | * @param MethodCall $node
73 | */
74 | public function refactor(Node $node): ?Node
75 | {
76 | $map = self::RENAME_METHODS_WITH_OBJECT_MAP;
77 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, array_keys($map))) {
78 | return null;
79 | }
80 |
81 | if ($node->isFirstClassCallable() || ! isset($node->getArgs()[0], $node->getArgs()[1])) {
82 | return null;
83 | }
84 |
85 | $firstNode = new Arg($node->getArgs()[0]->value);
86 |
87 | if ($node->getArgs()[1]->value instanceof ClassConstFetch) {
88 | $secondNode = $node->getArgs()[1];
89 | } else {
90 | $secondNode = new Arg($node->getArgs()[1]->value);
91 | }
92 |
93 | $funcCall = new FuncCall(new Name('property_exists'), [$secondNode, $firstNode]);
94 |
95 | $newArgs = $this->nodeFactory->createArgs([$funcCall]);
96 |
97 | unset($node->args[0], $node->args[1]);
98 | $node->args = array_merge($newArgs, $node->getArgs());
99 |
100 | $this->identifierManipulator->renameNodeWithMap($node, $map);
101 |
102 | return $node;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/rules/PHPUnit100/Rector/StmtsAwareInterface/ExpectsMethodCallDecorator.php:
--------------------------------------------------------------------------------
1 | expects(...)
29 | * with
30 | * $expects = ...
31 | *
32 | * @param Expression $expression
33 | */
34 | public function decorate(Expression $expression): MethodCall|StaticCall|null
35 | {
36 | /** @var MethodCall|StaticCall|null $expectsExactlyCall */
37 | $expectsExactlyCall = null;
38 |
39 | $this->simpleCallableNodeTraverser->traverseNodesWithCallable($expression, function (Node $node) use (
40 | &$expectsExactlyCall
41 | ): ?MethodCall {
42 | if (! $node instanceof MethodCall) {
43 | return null;
44 | }
45 |
46 | if (! $this->nodeNameResolver->isName($node->name, 'expects')) {
47 | return null;
48 | }
49 |
50 | if ($node->isFirstClassCallable()) {
51 | return null;
52 | }
53 |
54 | $firstArg = $node->getArgs()[0];
55 | if (! $firstArg->value instanceof MethodCall && ! $firstArg->value instanceof StaticCall) {
56 | return null;
57 | }
58 |
59 | $expectsExactlyCall = $firstArg->value;
60 |
61 | $node->args = [new Arg(new Variable(ConsecutiveVariable::MATCHER))];
62 |
63 | return $node;
64 | });
65 |
66 | // add expects() method
67 | if (! $expectsExactlyCall instanceof Expr) {
68 | $this->simpleCallableNodeTraverser->traverseNodesWithCallable($expression, function (Node $node): ?int {
69 | if (! $node instanceof MethodCall) {
70 | return null;
71 | }
72 |
73 | if ($node->var instanceof MethodCall) {
74 | return null;
75 | }
76 |
77 | $node->var = new MethodCall($node->var, 'expects', [
78 | new Arg(new Variable(ConsecutiveVariable::MATCHER)),
79 | ]);
80 |
81 | return NodeVisitor::STOP_TRAVERSAL;
82 | });
83 | }
84 |
85 | return $expectsExactlyCall;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/rules/PHPUnit120/Rector/Class_/RemoveOverrideFinalConstructTestCaseRector.php:
--------------------------------------------------------------------------------
1 | >
59 | */
60 | public function getNodeTypes(): array
61 | {
62 | return [Class_::class];
63 | }
64 |
65 | /**
66 | * @param Class_ $node
67 | */
68 | public function refactor(Node $node): Node|null
69 | {
70 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
71 | return null;
72 | }
73 |
74 | $constructClassMethod = $node->getMethod(MethodName::CONSTRUCT);
75 |
76 | if ($constructClassMethod instanceof ClassMethod) {
77 | foreach ($node->stmts as $key => $stmt) {
78 | if ($stmt instanceof ClassMethod && $this->isName($stmt, MethodName::CONSTRUCT)) {
79 | unset($node->stmts[$key]);
80 |
81 | $node->setAttribute('hasRemovedFinalConstruct', true);
82 | return $node;
83 | }
84 | }
85 | }
86 |
87 | return null;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/rules/PHPUnit50/Rector/StaticCall/GetMockRector.php:
--------------------------------------------------------------------------------
1 | getMock("Class");
44 | }
45 | }
46 | CODE_SAMPLE
47 | ,
48 | <<<'CODE_SAMPLE'
49 | use PHPUnit\Framework\TestCase;
50 |
51 | final class SomeTest extends TestCase
52 | {
53 | public function test()
54 | {
55 | $classMock = $this->createMock("Class");
56 | }
57 | }
58 | CODE_SAMPLE
59 | ), ]);
60 | }
61 |
62 | /**
63 | * @return array>
64 | */
65 | public function getNodeTypes(): array
66 | {
67 | return [MethodCall::class, StaticCall::class];
68 | }
69 |
70 | /**
71 | * @param MethodCall|StaticCall $node
72 | */
73 | public function refactor(Node $node): ?Node
74 | {
75 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames(
76 | $node,
77 | ['getMock', 'getMockWithoutInvokingTheOriginalConstructor']
78 | )) {
79 | return null;
80 | }
81 |
82 | if ($node instanceof MethodCall && $node->var instanceof MethodCall) {
83 | return null;
84 | }
85 |
86 | $classReflection = $this->reflectionResolver->resolveClassReflectionSourceObject($node);
87 | if ($classReflection instanceof ClassReflection && $classReflection->getName() !== 'PHPUnit\Framework\TestCase') {
88 | return null;
89 | }
90 |
91 | if ($node->isFirstClassCallable()) {
92 | return null;
93 | }
94 |
95 | // narrow args to one
96 | if (count($node->args) > 1) {
97 | $node->args = [$node->getArgs()[0]];
98 | }
99 |
100 | $node->name = new Identifier('createMock');
101 |
102 | return $node;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/rules/PHPUnit60/Rector/ClassMethod/ExceptionAnnotationRector.php:
--------------------------------------------------------------------------------
1 |
31 | */
32 | private const ANNOTATION_TO_METHOD = [
33 | 'expectedExceptionMessageRegExp' => 'expectExceptionMessageRegExp',
34 | 'expectedExceptionMessage' => 'expectExceptionMessage',
35 | 'expectedExceptionCode' => 'expectExceptionCode',
36 | 'expectedException' => 'expectException',
37 | ];
38 |
39 | public function __construct(
40 | private readonly ExpectExceptionMethodCallFactory $expectExceptionMethodCallFactory,
41 | private readonly PhpDocTagRemover $phpDocTagRemover,
42 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
43 | private readonly DocBlockUpdater $docBlockUpdater,
44 | private readonly PhpDocInfoFactory $phpDocInfoFactory,
45 | ) {
46 | }
47 |
48 | public function getRuleDefinition(): RuleDefinition
49 | {
50 | return new RuleDefinition(
51 | 'Changes `@expectedException annotations to `expectException*()` methods',
52 | [
53 | new CodeSample(
54 | <<<'CODE_SAMPLE'
55 | /**
56 | * @expectedException Exception
57 | * @expectedExceptionMessage Message
58 | */
59 | public function test()
60 | {
61 | // tested code
62 | }
63 | CODE_SAMPLE
64 | ,
65 | <<<'CODE_SAMPLE'
66 | public function test()
67 | {
68 | $this->expectException('Exception');
69 | $this->expectExceptionMessage('Message');
70 | // tested code
71 | }
72 | CODE_SAMPLE
73 | ),
74 | ]
75 | );
76 | }
77 |
78 | /**
79 | * @return array>
80 | */
81 | public function getNodeTypes(): array
82 | {
83 | return [ClassMethod::class];
84 | }
85 |
86 | /**
87 | * @param ClassMethod $node
88 | */
89 | public function refactor(Node $node): ?Node
90 | {
91 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
92 | return null;
93 | }
94 |
95 | $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
96 | if (! $phpDocInfo instanceof PhpDocInfo) {
97 | return null;
98 | }
99 |
100 | $hasChanged = false;
101 |
102 | foreach (self::ANNOTATION_TO_METHOD as $annotationName => $methodName) {
103 | if (! $phpDocInfo->hasByName($annotationName)) {
104 | continue;
105 | }
106 |
107 | $methodCallExpressions = $this->expectExceptionMethodCallFactory->createFromTagValueNodes(
108 | $phpDocInfo->getTagsByName($annotationName),
109 | $methodName,
110 | );
111 | $node->stmts = [...$methodCallExpressions, ...(array) $node->stmts];
112 |
113 | $this->phpDocTagRemover->removeByName($phpDocInfo, $annotationName);
114 | $hasChanged = true;
115 | }
116 |
117 | if (! $hasChanged) {
118 | return null;
119 | }
120 |
121 | $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node);
122 |
123 | return $node;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/rules/PHPUnit60/Rector/MethodCall/GetMockBuilderGetMockToCreateMockRector.php:
--------------------------------------------------------------------------------
1 | getMockBuilder('SomeClass')
44 | ->disableOriginalConstructor()
45 | ->getMock();
46 | }
47 | }
48 | CODE_SAMPLE
49 | ,
50 | <<<'CODE_SAMPLE'
51 | use PHPUnit\Framework\TestCase;
52 |
53 | final class SomeTest extends TestCase
54 | {
55 | public function test()
56 | {
57 | $applicationMock = $this->createMock('SomeClass');
58 | }
59 | }
60 | CODE_SAMPLE
61 | ),
62 | ]);
63 | }
64 |
65 | /**
66 | * @return array>
67 | */
68 | public function getNodeTypes(): array
69 | {
70 | return [MethodCall::class];
71 | }
72 |
73 | /**
74 | * @param MethodCall $node
75 | */
76 | public function refactor(Node $node): ?Node
77 | {
78 | if (! $this->isName($node->name, 'getMock')) {
79 | return null;
80 | }
81 |
82 | if (! $node->var instanceof MethodCall) {
83 | return null;
84 | }
85 |
86 | // traverse up over useless methods until we reach the top one
87 | $currentMethodCall = $node->var;
88 |
89 | while ($currentMethodCall instanceof MethodCall && $this->isNames(
90 | $currentMethodCall->name,
91 | self::USELESS_METHOD_NAMES
92 | )) {
93 | $currentMethodCall = $currentMethodCall->var;
94 | }
95 |
96 | if (! $currentMethodCall instanceof MethodCall) {
97 | return null;
98 | }
99 |
100 | // can be only local call, as createMock() is protected method
101 | if (! $this->isLocalScopeCaller($currentMethodCall)) {
102 | return null;
103 | }
104 |
105 | // must be be test case class
106 | if (! $this->isObjectType($currentMethodCall->var, new ObjectType('PHPUnit\Framework\TestCase'))) {
107 | return null;
108 | }
109 |
110 | if (! $this->isName($currentMethodCall->name, 'getMockBuilder')) {
111 | return null;
112 | }
113 |
114 | $args = $currentMethodCall->args;
115 | $thisVariable = $currentMethodCall->var;
116 |
117 | return new MethodCall($thisVariable, 'createMock', $args);
118 | }
119 |
120 | private function isLocalScopeCaller(MethodCall $currentMethodCall): bool
121 | {
122 | if (! $currentMethodCall->var instanceof Variable) {
123 | return false;
124 | }
125 |
126 | return $currentMethodCall->var->name === 'this';
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/rules/PHPUnit70/Rector/Class_/RemoveDataProviderTestPrefixRector.php:
--------------------------------------------------------------------------------
1 | >
80 | */
81 | public function getNodeTypes(): array
82 | {
83 | return [Class_::class];
84 | }
85 |
86 | /**
87 | * @param Class_ $node
88 | */
89 | public function refactor(Node $node): ?Node
90 | {
91 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
92 | return null;
93 | }
94 |
95 | $hasChanged = false;
96 |
97 | $dataProviderClassMethods = $this->dataProviderClassMethodFinder->find($node);
98 | foreach ($dataProviderClassMethods as $dataProviderClassMethod) {
99 | $dataProviderClassMethodName = $dataProviderClassMethod->name->toString();
100 |
101 | if (! str_starts_with($dataProviderClassMethodName, 'test')) {
102 | continue;
103 | }
104 |
105 | $shortMethodName = Strings::substring($dataProviderClassMethodName, 4);
106 | $shortMethodName = lcfirst($shortMethodName);
107 |
108 | $dataProviderClassMethod->name = new Identifier($shortMethodName);
109 | $hasChanged = true;
110 | }
111 |
112 | $this->dataProviderMethodRenamer->removeTestPrefix($node);
113 |
114 | if ($hasChanged) {
115 | return $node;
116 | }
117 |
118 | return null;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/rules/PHPUnit80/Rector/MethodCall/SpecificAssertContainsRector.php:
--------------------------------------------------------------------------------
1 |
28 | */
29 | private const OLD_TO_NEW_METHOD_NAMES = [
30 | 'assertContains' => 'assertStringContainsString',
31 | 'assertNotContains' => 'assertStringNotContainsString',
32 | ];
33 |
34 | public function __construct(
35 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
36 | ) {
37 | }
38 |
39 | public function getRuleDefinition(): RuleDefinition
40 | {
41 | return new RuleDefinition(
42 | 'Change assertContains()/assertNotContains() method to new string and iterable alternatives',
43 | [
44 | new CodeSample(
45 | <<<'CODE_SAMPLE'
46 | final class SomeTest extends \PHPUnit\Framework\TestCase
47 | {
48 | public function test()
49 | {
50 | $this->assertContains('foo', 'foo bar');
51 | $this->assertNotContains('foo', 'foo bar');
52 | }
53 | }
54 | CODE_SAMPLE
55 | ,
56 | <<<'CODE_SAMPLE'
57 | final class SomeTest extends \PHPUnit\Framework\TestCase
58 | {
59 | public function test()
60 | {
61 | $this->assertStringContainsString('foo', 'foo bar');
62 | $this->assertStringNotContainsString('foo', 'foo bar');
63 | }
64 | }
65 | CODE_SAMPLE
66 | ),
67 | ]
68 | );
69 | }
70 |
71 | /**
72 | * @return array>
73 | */
74 | public function getNodeTypes(): array
75 | {
76 | return [MethodCall::class, StaticCall::class];
77 | }
78 |
79 | /**
80 | * @param MethodCall|StaticCall $node
81 | */
82 | public function refactor(Node $node): ?Node
83 | {
84 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertContains', 'assertNotContains'])) {
85 | return null;
86 | }
87 |
88 | if ($node->isFirstClassCallable()) {
89 | return null;
90 | }
91 |
92 | if (! $this->isPossiblyStringType($node->getArgs()[1]->value)) {
93 | return null;
94 | }
95 |
96 | $methodName = $this->getName($node->name);
97 | $newMethodName = self::OLD_TO_NEW_METHOD_NAMES[$methodName];
98 | $node->name = new Identifier($newMethodName);
99 |
100 | return $node;
101 | }
102 |
103 | private function isPossiblyStringType(Expr $expr): bool
104 | {
105 | $exprType = $this->getType($expr);
106 |
107 | if ($exprType instanceof UnionType) {
108 | foreach ($exprType->getTypes() as $unionedType) {
109 | if ($unionedType instanceof StringType) {
110 | return true;
111 | }
112 | }
113 | }
114 |
115 | return $exprType instanceof StringType;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/rules/PHPUnit80/Rector/MethodCall/SpecificAssertInternalTypeRector.php:
--------------------------------------------------------------------------------
1 |
27 | */
28 | private const TYPE_TO_METHOD = [
29 | 'array' => ['assertIsArray', 'assertIsNotArray'],
30 | 'bool' => ['assertIsBool', 'assertIsNotBool'],
31 | 'boolean' => ['assertIsBool', 'assertIsNotBool'],
32 | 'float' => ['assertIsFloat', 'assertIsNotFloat'],
33 | 'int' => ['assertIsInt', 'assertIsNotInt'],
34 | 'integer' => ['assertIsInt', 'assertIsNotInt'],
35 | 'numeric' => ['assertIsNumeric', 'assertIsNotNumeric'],
36 | 'object' => ['assertIsObject', 'assertIsNotObject'],
37 | 'resource' => ['assertIsResource', 'assertIsNotResource'],
38 | 'string' => ['assertIsString', 'assertIsNotString'],
39 | 'scalar' => ['assertIsScalar', 'assertIsNotScalar'],
40 | 'callable' => ['assertIsCallable', 'assertIsNotCallable'],
41 | 'iterable' => ['assertIsIterable', 'assertIsNotIterable'],
42 | 'null' => ['assertNull', 'assertNotNull'],
43 | ];
44 |
45 | public function __construct(
46 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer
47 | ) {
48 | }
49 |
50 | public function getRuleDefinition(): RuleDefinition
51 | {
52 | return new RuleDefinition(
53 | 'Change assertInternalType()/assertNotInternalType() method to new specific alternatives',
54 | [
55 | new CodeSample(
56 | <<<'CODE_SAMPLE'
57 | final class SomeTest extends \PHPUnit\Framework\TestCase
58 | {
59 | public function test()
60 | {
61 | $value = 'value';
62 | $this->assertInternalType('string', $value);
63 | $this->assertNotInternalType('array', $value);
64 | }
65 | }
66 | CODE_SAMPLE
67 | ,
68 | <<<'CODE_SAMPLE'
69 | final class SomeTest extends \PHPUnit\Framework\TestCase
70 | {
71 | public function test()
72 | {
73 | $value = 'value';
74 | $this->assertIsString($value);
75 | $this->assertIsNotArray($value);
76 | }
77 | }
78 | CODE_SAMPLE
79 | ),
80 | ]
81 | );
82 | }
83 |
84 | /**
85 | * @return array>
86 | */
87 | public function getNodeTypes(): array
88 | {
89 | return [MethodCall::class, StaticCall::class];
90 | }
91 |
92 | /**
93 | * @param MethodCall|StaticCall $node
94 | */
95 | public function refactor(Node $node): ?Node
96 | {
97 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames(
98 | $node,
99 | ['assertInternalType', 'assertNotInternalType']
100 | )) {
101 | return null;
102 | }
103 |
104 | if ($node->isFirstClassCallable()) {
105 | return null;
106 | }
107 |
108 | $typeNode = $node->getArgs()[0]
109 | ->value;
110 | if (! $typeNode instanceof String_) {
111 | return null;
112 | }
113 |
114 | $type = $typeNode->value;
115 | if (! isset(self::TYPE_TO_METHOD[$type])) {
116 | return null;
117 | }
118 |
119 | array_shift($node->args);
120 |
121 | $position = $this->isName($node->name, 'assertInternalType') ? 0 : 1;
122 | $methodName = self::TYPE_TO_METHOD[$type][$position];
123 |
124 | $node->name = new Identifier($methodName);
125 |
126 | return $node;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/rules/PHPUnit90/Rector/MethodCall/ReplaceAtMethodWithDesiredMatcherRector.php:
--------------------------------------------------------------------------------
1 | expects($this->at(0))
37 | ->method('foo')
38 | ->willReturn('1');
39 | CODE_SAMPLE
40 | ,
41 | <<<'CODE_SAMPLE'
42 | $mock->expects($this->never())
43 | ->method('foo')
44 | ->willReturn('1');
45 | CODE_SAMPLE
46 | ),
47 | ]
48 | );
49 | }
50 |
51 | /**
52 | * @return array>
53 | */
54 | public function getNodeTypes(): array
55 | {
56 | return [MethodCall::class];
57 | }
58 |
59 | /**
60 | * @param MethodCall $node
61 | */
62 | public function refactor(Node $node): null|MethodCall
63 | {
64 | $this->hasChanged = false;
65 |
66 | if (! $this->testsNodeAnalyzer->isInTestClass($node)) {
67 | return null;
68 | }
69 |
70 | if ($node->var instanceof MethodCall && $arg = $this->findAtMethodCall($node->var)) {
71 | $this->replaceWithDesiredMatcher($arg);
72 | }
73 |
74 | if ($this->hasChanged) {
75 | return $node;
76 | }
77 |
78 | return null;
79 | }
80 |
81 | private function findAtMethodCall(MethodCall $methodCall): ?Arg
82 | {
83 | foreach ($methodCall->getArgs() as $arg) {
84 | if ($arg->value instanceof MethodCall &&
85 | $arg->value->name instanceof Identifier &&
86 | $arg->value->name->toString() === 'at'
87 | ) {
88 | return $arg;
89 | }
90 | }
91 |
92 | if ($methodCall->var instanceof MethodCall) {
93 | $this->findAtMethodCall($methodCall->var);
94 | }
95 |
96 | return null;
97 | }
98 |
99 | private function replaceWithDesiredMatcher(Arg $arg): void
100 | {
101 | if (! $arg->value instanceof MethodCall) {
102 | return;
103 | }
104 |
105 | foreach ($arg->value->getArgs() as $item) {
106 | if ($item->value instanceof Int_) {
107 | $count = $item->value->value;
108 | }
109 | }
110 |
111 | if (! isset($count)) {
112 | return;
113 | }
114 |
115 | if ($count === 0) {
116 | $arg->value = new MethodCall($arg->value->var, 'never');
117 | $this->hasChanged = true;
118 | } elseif ($count === 1) {
119 | $arg->value = new MethodCall($arg->value->var, 'once');
120 | $this->hasChanged = true;
121 | } elseif ($count > 1) {
122 | $arg->value = new MethodCall($arg->value->var, 'exactly', [new Arg(new Int_($count))]);
123 | $this->hasChanged = true;
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/rules/PHPUnit90/Rector/MethodCall/SpecificAssertContainsWithoutIdentityRector.php:
--------------------------------------------------------------------------------
1 | >
26 | */
27 | private const OLD_METHODS_NAMES_TO_NEW_NAMES = [
28 | 'string' => [
29 | 'assertContains' => 'assertContainsEquals',
30 | 'assertNotContains' => 'assertNotContainsEquals',
31 | ],
32 | ];
33 |
34 | public function __construct(
35 | private readonly TestsNodeAnalyzer $testsNodeAnalyzer,
36 | private readonly ValueResolver $valueResolver
37 | ) {
38 | }
39 |
40 | public function getRuleDefinition(): RuleDefinition
41 | {
42 | return new RuleDefinition(
43 | 'Change assertContains()/assertNotContains() with non-strict comparison to new specific alternatives',
44 | [
45 | new CodeSample(
46 | <<<'CODE_SAMPLE'
47 | final class SomeTest extends \PHPUnit\Framework\TestCase
48 | {
49 | public function test()
50 | {
51 | $objects = [ new \stdClass(), new \stdClass(), new \stdClass() ];
52 | $this->assertContains(new \stdClass(), $objects, 'message', false, false);
53 | $this->assertNotContains(new \stdClass(), $objects, 'message', false, false);
54 | }
55 | }
56 | CODE_SAMPLE
57 | ,
58 | <<<'CODE_SAMPLE'
59 | final class SomeTest extends TestCase
60 | {
61 | public function test()
62 | {
63 | $objects = [ new \stdClass(), new \stdClass(), new \stdClass() ];
64 | $this->assertContainsEquals(new \stdClass(), $objects, 'message');
65 | $this->assertNotContainsEquals(new \stdClass(), $objects, 'message');
66 | }
67 | }
68 | CODE_SAMPLE
69 | ),
70 | ]
71 | );
72 | }
73 |
74 | /**
75 | * @return array>
76 | */
77 | public function getNodeTypes(): array
78 | {
79 | return [MethodCall::class, StaticCall::class];
80 | }
81 |
82 | /**
83 | * @param MethodCall|StaticCall $node
84 | */
85 | public function refactor(Node $node): ?Node
86 | {
87 | if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertContains', 'assertNotContains'])) {
88 | return null;
89 | }
90 |
91 | if ($node->isFirstClassCallable()) {
92 | return null;
93 | }
94 |
95 | if (count($node->getArgs()) < 2) {
96 | return null;
97 | }
98 |
99 | // when second argument is string: do nothing
100 | $secondArgType = $this->getType($node->getArgs()[1]->value);
101 | if ($secondArgType instanceof StringType) {
102 | return null;
103 | }
104 |
105 | //when less then 5 arguments given: do nothing
106 | if (! isset($node->getArgs()[4])) {
107 | return null;
108 | }
109 |
110 | $fourthArg = $node->getArgs()[4];
111 |
112 | //when 5th argument check identity is true: do nothing
113 | if ($this->valueResolver->isValue($fourthArg->value, true)) {
114 | return null;
115 | }
116 |
117 | /* here we search for element of array without identity check and we can replace functions */
118 | $methodName = $this->getName($node->name);
119 |
120 | $node->name = new Identifier(self::OLD_METHODS_NAMES_TO_NEW_NAMES['string'][$methodName]);
121 | unset($node->args[3], $node->args[4]);
122 |
123 | return $node;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/Composer/ProjectPackageVersionResolver.php:
--------------------------------------------------------------------------------
1 | installedPackageResolver = new InstalledPackageResolver(getcwd());
22 | }
23 |
24 | public function findPackageVersion(string $packageName): ?string
25 | {
26 | $rootProjectInstalledPackages = $this->installedPackageResolver->resolve();
27 |
28 | foreach ($rootProjectInstalledPackages as $rootProjectInstalledPackage) {
29 | if ($rootProjectInstalledPackage->getName() === $packageName) {
30 | return $rootProjectInstalledPackage->getVersion();
31 | }
32 | }
33 |
34 | return null;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Enum/AssertMethod.php:
--------------------------------------------------------------------------------
1 | simpleCallableNodeTraverser->traverseNodesWithCallable($expression, function (Node $node) use (
24 | $methodName
25 | ): ?Node {
26 | if (! $node instanceof MethodCall) {
27 | return null;
28 | }
29 |
30 | if (! $this->nodeNameResolver->isName($node->name, $methodName)) {
31 | return null;
32 | }
33 |
34 | return $node->var;
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Naming/TestClassNameResolver.php:
--------------------------------------------------------------------------------
1 | isFirstClassCallable()) {
15 | return;
16 | }
17 |
18 | $methodArguments = $node->getArgs();
19 | array_shift($methodArguments);
20 |
21 | $node->args = $methodArguments;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/NodeAnalyzer/IdentifierManipulator.php:
--------------------------------------------------------------------------------
1 | $renameMethodMap
30 | */
31 | public function renameNodeWithMap(
32 | ClassConstFetch | MethodCall | PropertyFetch | StaticCall | ClassMethod $node,
33 | array $renameMethodMap
34 | ): bool {
35 | $oldNodeMethodName = $this->resolveOldMethodName($node);
36 | if (! is_string($oldNodeMethodName)) {
37 | return false;
38 | }
39 |
40 | $node->name = new Identifier($renameMethodMap[$oldNodeMethodName]);
41 | return true;
42 | }
43 |
44 | private function resolveOldMethodName(
45 | ClassConstFetch | MethodCall | PropertyFetch | StaticCall | ClassMethod $node
46 | ): ?string {
47 | if ($node instanceof StaticCall || $node instanceof MethodCall) {
48 | return $this->nodeNameResolver->getName($node->name);
49 | }
50 |
51 | return $this->nodeNameResolver->getName($node);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/NodeAnalyzer/MockedVariableAnalyzer.php:
--------------------------------------------------------------------------------
1 | simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod, function (Node $node) use (
34 | &$doesContainMock
35 | ): null {
36 | if ($this->isMockeryStaticCall($node)) {
37 | $doesContainMock = true;
38 | return null;
39 | }
40 |
41 | if (! $node instanceof PropertyFetch && ! $node instanceof Variable) {
42 | return null;
43 | }
44 |
45 | $variableType = $this->nodeTypeResolver->getType($node);
46 | if ($variableType instanceof MixedType) {
47 | return null;
48 | }
49 |
50 | if ($this->isIntersectionTypeWithMockObject($variableType)) {
51 | $doesContainMock = true;
52 | }
53 |
54 | if ($variableType->isSuperTypeOf(new ObjectType('PHPUnit\Framework\MockObject\MockObject'))->yes()) {
55 | $doesContainMock = true;
56 | }
57 |
58 | return null;
59 | });
60 |
61 | return $doesContainMock;
62 | }
63 |
64 | private function isIntersectionTypeWithMockObject(Type $variableType): bool
65 | {
66 | if ($variableType instanceof IntersectionType) {
67 | foreach ($variableType->getTypes() as $variableTypeType) {
68 | if ($variableTypeType->isSuperTypeOf(
69 | new ObjectType('PHPUnit\Framework\MockObject\MockObject')
70 | )->yes()) {
71 | return true;
72 | }
73 | }
74 | }
75 |
76 | return false;
77 | }
78 |
79 | private function isMockeryStaticCall(Node $node): bool
80 | {
81 | if (! $node instanceof StaticCall) {
82 | return false;
83 | }
84 |
85 | // is mockery mock
86 | if (! $this->nodeNameResolver->isName($node->class, 'Mockery')) {
87 | return false;
88 | }
89 |
90 | return $this->nodeNameResolver->isName($node->name, 'mock');
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/NodeAnalyzer/SetUpMethodDecorator.php:
--------------------------------------------------------------------------------
1 | astResolver->resolveClassMethod('PHPUnit\Framework\TestCase', MethodName::SET_UP);
31 | if (! $setUpClassMethod instanceof ClassMethod) {
32 | return;
33 | }
34 |
35 | if ($setUpClassMethod->returnType instanceof Identifier) {
36 | $classMethod->returnType = new Identifier($setUpClassMethod->returnType->toString());
37 | return;
38 | }
39 |
40 | $classMethod->returnType = null;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/NodeAnalyzer/TestsNodeAnalyzer.php:
--------------------------------------------------------------------------------
1 | reflectionResolver->resolveClassReflection($node);
37 |
38 | if (! $classReflection instanceof ClassReflection) {
39 | return false;
40 | }
41 |
42 | foreach (self::TEST_CASE_OBJECT_CLASSES as $testCaseObjectClass) {
43 | if ($classReflection->is($testCaseObjectClass)) {
44 | return true;
45 | }
46 | }
47 |
48 | return false;
49 | }
50 |
51 | public function isTestClassMethod(ClassMethod $classMethod): bool
52 | {
53 | if (! $classMethod->isPublic()) {
54 | return false;
55 | }
56 |
57 | if (str_starts_with($classMethod->name->toString(), 'test')) {
58 | return true;
59 | }
60 |
61 | foreach ($classMethod->getAttrGroups() as $attributeGroup) {
62 | foreach ($attributeGroup->attrs as $attribute) {
63 | if ($attribute->name->toString() === 'PHPUnit\\Framework\\Attributes\\Test') {
64 | return true;
65 | }
66 | }
67 | }
68 |
69 | $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
70 | return $phpDocInfo->hasByName('test');
71 | }
72 |
73 | public function isAssertMethodCallName(Node $node, string $name): bool
74 | {
75 | if ($node instanceof StaticCall) {
76 | $callerType = $this->nodeTypeResolver->getType($node->class);
77 | } elseif ($node instanceof MethodCall) {
78 | $callerType = $this->nodeTypeResolver->getType($node->var);
79 | } else {
80 | return false;
81 | }
82 |
83 | $assertObjectType = new ObjectType('PHPUnit\Framework\Assert');
84 | if (! $assertObjectType->isSuperTypeOf($callerType)
85 | ->yes()) {
86 | return false;
87 | }
88 |
89 | /** @var StaticCall|MethodCall $node */
90 | return $this->nodeNameResolver->isName($node->name, $name);
91 | }
92 |
93 | /**
94 | * @param string[] $names
95 | */
96 | public function isPHPUnitMethodCallNames(Node $node, array $names): bool
97 | {
98 | if (! $this->isPHPUnitTestCaseCall($node)) {
99 | return false;
100 | }
101 |
102 | /** @var MethodCall|StaticCall $node */
103 | return $this->nodeNameResolver->isNames($node->name, $names);
104 | }
105 |
106 | public function isPHPUnitTestCaseCall(Node $node): bool
107 | {
108 | if ($node instanceof MethodCall) {
109 | return $this->isInTestClass($node);
110 | }
111 |
112 | if ($node instanceof StaticCall) {
113 | $classType = $this->nodeTypeResolver->getType($node->class);
114 | if ($classType instanceof ObjectType) {
115 | if ($classType->isInstanceOf(PHPUnitClassName::TEST_CASE)->yes()) {
116 | return true;
117 | }
118 |
119 | if ($classType->isInstanceOf(PHPUnitClassName::ASSERT)->yes()) {
120 | return true;
121 | }
122 | }
123 | }
124 |
125 | return false;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/NodeFactory/AssertCallFactory.php:
--------------------------------------------------------------------------------
1 | var, $name);
16 | }
17 |
18 | return new StaticCall($node->class, $name);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/NodeFactory/ExpectExceptionMethodCallFactory.php:
--------------------------------------------------------------------------------
1 | createMethodCall($phpDocTagNode, $methodName);
35 | $methodCallExpressions[] = new Expression($methodCall);
36 | }
37 |
38 | return $methodCallExpressions;
39 | }
40 |
41 | private function createMethodCall(PhpDocTagNode $phpDocTagNode, string $methodName): MethodCall
42 | {
43 | if (! $phpDocTagNode->value instanceof GenericTagValueNode) {
44 | throw new ShouldNotHappenException();
45 | }
46 |
47 | $expr = $this->createExpectedExpr($phpDocTagNode, $phpDocTagNode->value);
48 | return $this->nodeFactory->createMethodCall('this', $methodName, [new Arg($expr)]);
49 | }
50 |
51 | private function createExpectedExpr(PhpDocTagNode $phpDocTagNode, GenericTagValueNode $genericTagValueNode): Expr
52 | {
53 | if ($phpDocTagNode->name === '@expectedExceptionMessage') {
54 | return new String_($genericTagValueNode->value);
55 | }
56 |
57 | return $this->phpDocValueToNodeMapper->mapGenericTagValueNode($genericTagValueNode);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/NodeFactory/MatcherInvocationCountMethodCallNodeFactory.php:
--------------------------------------------------------------------------------
1 | getInvocationMethodName();
23 |
24 | $matcherVariable = new Variable(ConsecutiveVariable::MATCHER);
25 |
26 | return new MethodCall($matcherVariable, new Identifier($invocationMethodName));
27 | }
28 |
29 | private function getInvocationMethodName(): string
30 | {
31 | $projectPHPUnitVersion = $this->projectPackageVersionResolver->findPackageVersion('phpunit/phpunit');
32 |
33 | if ($projectPHPUnitVersion === null || version_compare($projectPHPUnitVersion, '10.0', '>=')) {
34 | // phpunit 10 naming
35 | return 'numberOfInvocations';
36 | }
37 |
38 | // phpunit 9 naming
39 | return 'getInvocationCount';
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/NodeFactory/UsedVariablesResolver.php:
--------------------------------------------------------------------------------
1 | getArgs();
29 |
30 | $stmtVariables = $returnStmt instanceof Stmt ? $this->resolveUniqueVariables([$returnStmt]) : [];
31 |
32 | return $this->resolveUniqueVariables(array_merge($consecutiveArgs, $stmtVariables));
33 | }
34 |
35 | /**
36 | * @param Node[] $nodes
37 | * @return Variable[]
38 | */
39 | private function resolveUniqueVariables(array $nodes): array
40 | {
41 | /** @var Variable[] $usedVariables */
42 | $usedVariables = $this->betterNodeFinder->findInstancesOfScoped($nodes, Variable::class);
43 |
44 | $uniqueUsedVariables = [];
45 |
46 | foreach ($usedVariables as $usedVariable) {
47 | if ($this->nodeNameResolver->isNames(
48 | $usedVariable,
49 | ['this', ConsecutiveVariable::MATCHER, ConsecutiveVariable::PARAMETERS]
50 | )) {
51 | continue;
52 | }
53 |
54 | $usedVariableName = $this->nodeNameResolver->getName($usedVariable);
55 | $uniqueUsedVariables[$usedVariableName] = $usedVariable;
56 | }
57 |
58 | return $uniqueUsedVariables;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/NodeFinder/MethodCallNodeFinder.php:
--------------------------------------------------------------------------------
1 | betterNodeFinder->find($expression, function (Node $node) use (
27 | $methodNames
28 | ): bool {
29 | if (! $node instanceof MethodCall) {
30 | return false;
31 | }
32 |
33 | return $this->nodeNameResolver->isNames($node->name, $methodNames);
34 | });
35 |
36 | return $desiredMethodCalls !== [];
37 | }
38 |
39 | public function findByName(Expression $expression, string $methodName): ?MethodCall
40 | {
41 | if (! $expression->expr instanceof MethodCall) {
42 | return null;
43 | }
44 |
45 | /** @var MethodCall|null $methodCall */
46 | $methodCall = $this->betterNodeFinder->findFirst($expression->expr, function (Node $node) use (
47 | $methodName
48 | ): bool {
49 | if (! $node instanceof MethodCall) {
50 | return false;
51 | }
52 |
53 | return $this->nodeNameResolver->isName($node->name, $methodName);
54 | });
55 |
56 | return $methodCall;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/PhpDoc/DataProviderMethodRenamer.php:
--------------------------------------------------------------------------------
1 | getMethods() as $classMethod) {
25 | $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
26 |
27 | $hasClassMethodChanged = false;
28 |
29 | foreach ($phpDocInfo->getTagsByName('dataProvider') as $phpDocTagNode) {
30 | if (! $phpDocTagNode->value instanceof GenericTagValueNode) {
31 | continue;
32 | }
33 |
34 | $oldMethodName = $phpDocTagNode->value->value;
35 | if (! \str_starts_with($oldMethodName, 'test')) {
36 | continue;
37 | }
38 |
39 | $newMethodName = $this->createMethodNameWithoutPrefix($oldMethodName, 'test');
40 | $phpDocTagNode->value->value = Strings::replace(
41 | $oldMethodName,
42 | '#' . preg_quote($oldMethodName, '#') . '#',
43 | $newMethodName
44 | );
45 |
46 | // invoke reprint
47 | $phpDocTagNode->setAttribute(PhpDocAttributeKey::START_AND_END, null);
48 | $hasClassMethodChanged = true;
49 | }
50 |
51 | if ($hasClassMethodChanged) {
52 | $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod);
53 | }
54 | }
55 | }
56 |
57 | private function createMethodNameWithoutPrefix(string $methodName, string $prefix): string
58 | {
59 | $newMethodName = Strings::substring($methodName, strlen($prefix));
60 | return lcfirst($newMethodName);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/PhpDoc/PhpDocValueToNodeMapper.php:
--------------------------------------------------------------------------------
1 | value, '::')) {
25 | [$class, $constant] = explode('::', $genericTagValueNode->value);
26 |
27 | $name = new Name($class);
28 | return $this->nodeFactory->createClassConstFetchFromName($name, $constant);
29 | }
30 |
31 | $reference = ltrim($genericTagValueNode->value, '\\');
32 |
33 | if ($this->reflectionProvider->hasClass($reference)) {
34 | return $this->nodeFactory->createClassConstReference($reference);
35 | }
36 |
37 | return new String_($reference);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Set/PHPUnitSetList.php:
--------------------------------------------------------------------------------
1 | $valueMap
11 | */
12 | public function __construct(
13 | private string $annotationName,
14 | private string $attributeClass,
15 | private array $valueMap = [],
16 | private bool $isOnClassLevel = false,
17 | ) {
18 | }
19 |
20 | public function getAnnotationName(): string
21 | {
22 | return $this->annotationName;
23 | }
24 |
25 | public function getAttributeClass(): string
26 | {
27 | return $this->attributeClass;
28 | }
29 |
30 | /**
31 | * @return array
32 | */
33 | public function getValueMap(): array
34 | {
35 | return $this->valueMap;
36 | }
37 |
38 | public function getIsOnClassLevel(): bool
39 | {
40 | return $this->isOnClassLevel;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/ValueObject/BinaryOpWithAssertMethod.php:
--------------------------------------------------------------------------------
1 | binaryOpClass;
19 | }
20 |
21 | public function getAssetMethodName(): string
22 | {
23 | return $this->assetMethodName;
24 | }
25 |
26 | public function getNotAssertMethodName(): string
27 | {
28 | return $this->notAssertMethodName;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/ValueObject/ConstantWithAssertMethods.php:
--------------------------------------------------------------------------------
1 | constant;
19 | }
20 |
21 | public function getAssetMethodName(): string
22 | {
23 | return $this->assetMethodName;
24 | }
25 |
26 | public function getNotAssertMethodName(): string
27 | {
28 | return $this->notAssertMethodName;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/ValueObject/FunctionNameWithAssertMethods.php:
--------------------------------------------------------------------------------
1 | functionName;
19 | }
20 |
21 | public function getAssetMethodName(): string
22 | {
23 | return $this->assetMethodName;
24 | }
25 |
26 | public function getNotAssertMethodName(): string
27 | {
28 | return $this->notAssertMethodName;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------