├── .github └── workflows │ ├── code_analysis.yaml │ ├── downgraded_release.yaml │ └── tests.yaml ├── LICENSE ├── README.md ├── banner.png ├── build ├── composer-php-74.json └── rector-downgrade-php-74.php ├── commands ├── MakeRuleCommand.php └── make-rule.php ├── composer.json ├── config ├── config.php └── sets │ ├── laravel-array-str-functions-to-static-call.php │ ├── laravel-arrayaccess-to-method-call.php │ ├── laravel-code-quality.php │ ├── laravel-collection.php │ ├── laravel-container-string-to-fully-qualified-name.php │ ├── laravel-eloquent-magic-method-to-query-builder.php │ ├── laravel-facade-aliases-to-full-names.php │ ├── laravel-if-helpers.php │ ├── laravel-legacy-factories-to-classes.php │ ├── laravel-static-to-injection.php │ ├── laravel100.php │ ├── laravel110.php │ ├── laravel120.php │ ├── laravel50.php │ ├── laravel51.php │ ├── laravel52.php │ ├── laravel53.php │ ├── laravel54.php │ ├── laravel55.php │ ├── laravel56.php │ ├── laravel57.php │ ├── laravel58.php │ ├── laravel60.php │ ├── laravel70.php │ ├── laravel80.php │ ├── laravel90.php │ ├── level │ ├── up-to-laravel-100.php │ ├── up-to-laravel-110.php │ ├── up-to-laravel-120.php │ ├── up-to-laravel-51.php │ ├── up-to-laravel-52.php │ ├── up-to-laravel-53.php │ ├── up-to-laravel-54.php │ ├── up-to-laravel-55.php │ ├── up-to-laravel-56.php │ ├── up-to-laravel-57.php │ ├── up-to-laravel-58.php │ ├── up-to-laravel-60.php │ ├── up-to-laravel-70.php │ ├── up-to-laravel-80.php │ └── up-to-laravel-90.php │ └── packages │ ├── cashier │ ├── cashier-13.php │ ├── cashier-14.php │ └── level │ │ ├── up-to-cashier-13.php │ │ └── up-to-cashier-14.php │ └── livewire │ ├── level │ └── up-to-livewire-30.php │ └── livewire-30.php ├── docs └── rector_rules_overview.md ├── rector.php ├── src ├── AbstractRector.php ├── Contract │ └── ValueObject │ │ └── ArgumentFuncCallToMethodCallInterface.php ├── NodeAnalyzer │ ├── ApplicationAnalyzer.php │ ├── ExpectedClassMethodAnalyzer.php │ ├── FacadeAssertionAnalyzer.php │ ├── LaravelServiceAnalyzer.php │ ├── LumenRouteRegisteringMethodAnalyzer.php │ ├── ModelAnalyzer.php │ ├── QueryBuilderAnalyzer.php │ └── StaticCallAnalyzer.php ├── NodeFactory │ ├── AppAssignFactory.php │ ├── DispatchableTestsMethodsFactory.php │ ├── ModelFactoryNodeFactory.php │ └── RouterRegisterNodeAnalyzer.php ├── Rector │ ├── ArrayDimFetch │ │ ├── EnvVariableToEnvHelperRector.php │ │ ├── RequestVariablesToRequestFacadeRector.php │ │ ├── ServerVariableToRequestFacadeRector.php │ │ └── SessionVariableToSessionFacadeRector.php │ ├── Assign │ │ └── CallOnAppArrayAccessToStandaloneAssignRector.php │ ├── BooleanNot │ │ └── AvoidNegatedCollectionContainsOrDoesntContainRector.php │ ├── Cast │ │ └── DatabaseExpressionCastsToMethodCallRector.php │ ├── ClassMethod │ │ ├── AddArgumentDefaultValueRector.php │ │ ├── AddGenericReturnTypeToRelationsRector.php │ │ ├── AddParentBootToModelClassMethodRector.php │ │ ├── AddParentRegisterToEventServiceProviderRector.php │ │ ├── MigrateToSimplifiedAttributeRector.php │ │ └── ScopeNamedClassMethodToScopeAttributedClassMethodRector.php │ ├── Class_ │ │ ├── AddExtendsAnnotationToModelFactoriesRector.php │ │ ├── AddMockConsoleOutputFalseToConsoleTestsRector.php │ │ ├── AnonymousMigrationsRector.php │ │ ├── CashierStripeOptionsToStripeRector.php │ │ ├── LivewireComponentComputedMethodToComputedAttributeRector.php │ │ ├── LivewireComponentQueryStringToUrlAttributeRector.php │ │ ├── ModelCastsPropertyToCastsMethodRector.php │ │ ├── PropertyDeferToDeferrableProviderToRector.php │ │ ├── RemoveModelPropertyFromFactoriesRector.php │ │ ├── ReplaceExpectsMethodsInTestsRector.php │ │ └── UnifyModelDatesWithCastsRector.php │ ├── Coalesce │ │ └── ApplyDefaultInsteadOfNullCoalesceRector.php │ ├── Empty_ │ │ └── EmptyToBlankAndFilledFuncRector.php │ ├── Expr │ │ ├── AppEnvironmentComparisonToParameterRector.php │ │ └── SubStrToStartsWithOrEndsWithStaticMethodCallRector │ │ │ └── SubStrToStartsWithOrEndsWithStaticMethodCallRector.php │ ├── FuncCall │ │ ├── ArgumentFuncCallToMethodCallRector.php │ │ ├── DispatchNonShouldQueueToDispatchSyncRector.php │ │ ├── FactoryFuncCallToStaticCallRector.php │ │ ├── HelperFuncCallToFacadeClassRector.php │ │ ├── NotFilledBlankFuncCallToBlankFilledFuncCallRector.php │ │ ├── NowFuncWithStartOfDayMethodCallToTodayFuncRector.php │ │ ├── RemoveDumpDataDeadCodeRector.php │ │ ├── RemoveRedundantValueCallsRector.php │ │ ├── RemoveRedundantWithCallsRector.php │ │ ├── SleepFuncToSleepStaticCallRector.php │ │ ├── ThrowIfAndThrowUnlessExceptionsToUseClassStringRector.php │ │ └── TypeHintTappableCallRector.php │ ├── If_ │ │ ├── AbortIfRector.php │ │ ├── ReportIfRector.php │ │ └── ThrowIfRector.php │ ├── MethodCall │ │ ├── AssertSeeToAssertSeeHtmlRector.php │ │ ├── AssertStatusToAssertMethodRector.php │ │ ├── AvoidNegatedCollectionFilterOrRejectRector.php │ │ ├── ChangeQueryWhereDateValueWithCarbonRector.php │ │ ├── ContainerBindConcreteWithClosureOnlyRector.php │ │ ├── DatabaseExpressionToStringToMethodCallRector.php │ │ ├── EloquentOrderByToLatestOrOldestRector.php │ │ ├── EloquentWhereRelationTypeHintingParameterRector.php │ │ ├── EloquentWhereTypeHintClosureParameterRector.php │ │ ├── FactoryApplyingStatesRector.php │ │ ├── JsonCallToExplicitJsonCallRector.php │ │ ├── LumenRoutesStringActionToUsesArrayRector.php │ │ ├── LumenRoutesStringMiddlewareToArrayRector.php │ │ ├── RedirectBackToBackHelperRector.php │ │ ├── RedirectRouteToToRouteHelperRector.php │ │ ├── RefactorBlueprintGeometryColumnsRector.php │ │ ├── ReplaceServiceContainerCallArgRector.php │ │ ├── ReplaceWithoutJobsEventsAndNotificationsWithFacadeFakeRector.php │ │ ├── ResponseHelperCallToJsonResponseRector.php │ │ ├── ReverseConditionableMethodCallRector.php │ │ ├── UnaliasCollectionMethodsRector.php │ │ ├── UseComponentPropertyWithinCommandsRector.php │ │ ├── ValidationRuleArrayStringValueToArrayRector.php │ │ └── WhereToWhereLikeRector.php │ ├── Namespace_ │ │ └── FactoryDefinitionRector.php │ ├── New_ │ │ └── AddGuardToLoginEventRector.php │ ├── PropertyFetch │ │ ├── OptionalToNullsafeOperatorRector.php │ │ └── ReplaceFakerInstanceWithHelperRector.php │ └── StaticCall │ │ ├── AssertWithClassStringToTypeHintedClosureRector.php │ │ ├── CarbonSetTestNowToTravelToRector.php │ │ ├── DispatchToHelperFunctionsRector.php │ │ ├── EloquentMagicMethodToQueryBuilderRector.php │ │ ├── MinutesToSecondsInCacheRector.php │ │ ├── Redirect301ToPermanentRedirectRector.php │ │ ├── ReplaceAssertTimesSendWithAssertSentTimesRector.php │ │ ├── RequestStaticValidateToInjectRector.php │ │ └── RouteActionCallableRector.php ├── Set │ ├── LaravelLevelSetList.php │ ├── LaravelSetList.php │ ├── LaravelSetProvider.php │ └── Packages │ │ ├── Cashier │ │ ├── CashierLevelSetList.php │ │ └── CashierSetList.php │ │ └── Livewire │ │ ├── LivewireLevelSetList.php │ │ └── LivewireSetList.php └── ValueObject │ ├── AddArgumentDefaultValue.php │ ├── ApplyDefaultInsteadOfNullCoalesce.php │ ├── ArgumentFuncCallToMethodCall.php │ ├── ArrayFuncCallToMethodCall.php │ ├── ExpectedClassMethodMethodCalls.php │ ├── ReplaceRequestKeyAndMethodValue.php │ ├── ReplaceServiceContainerCallArg.php │ ├── ServiceNameTypeAndVariableName.php │ └── TypeToTimeMethodAndPosition.php └── templates ├── configurable-config.php.template ├── configurable-rule.php.template ├── fixture.php.inc.template ├── non-configurable-config.php.template ├── non-configurable-rule.php.template └── test.php.template /.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 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | actions: 20 | - 21 | name: 'Composer Validate' 22 | run: composer validate 23 | 24 | - 25 | name: 'Duster Lint' 26 | run: composer lint 27 | 28 | - 29 | name: 'Rector Dry Run' 30 | run: composer rector-dry-run 31 | 32 | - 33 | name: 'PHPStan' 34 | run: composer phpstan 35 | 36 | name: ${{ matrix.actions.name }} 37 | 38 | steps: 39 | - uses: actions/checkout@v4 40 | 41 | - uses: shivammathur/setup-php@v2 42 | with: 43 | php-version: 8.3 44 | coverage: none 45 | 46 | - uses: ramsey/composer-install@v2 47 | 48 | - run: ${{ matrix.actions.run }} 49 | -------------------------------------------------------------------------------- /.github/workflows/downgraded_release.yaml: -------------------------------------------------------------------------------- 1 | name: Downgraded Release 2 | 3 | # https://tomasvotruba.com/blog/how-to-release-php-81-and-72-package-in-the-same-repository/ 4 | # https://github.com/TomasVotruba/cognitive-complexity/blob/main/.github/workflows/downgraded_release.yaml 5 | # https://github.com/symplify/config-transformer/blob/main/.github/workflows/downgraded_release.yaml 6 | 7 | on: 8 | push: 9 | tags: 10 | - '*' 11 | 12 | jobs: 13 | downgrade_release: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - 18 | uses: "actions/checkout@v4" 19 | 20 | - 21 | uses: "shivammathur/setup-php@v2" 22 | with: 23 | php-version: 8.3 24 | coverage: none 25 | 26 | - uses: "ramsey/composer-install@v2" 27 | 28 | # downgrade /src to PHP 7.4 29 | - run: vendor/bin/rector process src config --config build/rector-downgrade-php-74.php --ansi 30 | 31 | # copy PHP 7.4 composer 32 | - run: cp build/composer-php-74.json composer.json 33 | 34 | # run the tests against the downgraded rules 35 | - run: vendor/bin/phpunit tests 36 | 37 | # clear the dev files 38 | - run: rm -rf build .github tests stubs phpstan.neon phpunit.xml 39 | 40 | # setup git user 41 | - 42 | run: | 43 | git config user.email "action@github.com" 44 | git config user.name "GitHub Action" 45 | 46 | # publish to the same repository with a new tag 47 | - 48 | name: "Tag Downgraded Code" 49 | run: | 50 | git commit -a -m "release PHP 7.4 downgraded ${GITHUB_REF#refs/tags/}" 51 | 52 | # force push tag, so there is only 1 version 53 | git tag "${GITHUB_REF#refs/tags/}" --force 54 | git push origin "${GITHUB_REF#refs/tags/}" --force 55 | -------------------------------------------------------------------------------- /.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: PHP Tests 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: 8.3 24 | coverage: none 25 | 26 | - uses: ramsey/composer-install@v2 27 | 28 | - run: vendor/bin/phpunit tests 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/driftingly/rector-laravel/4378a24ae08c035de31710872a3414363f5f53d6/banner.png -------------------------------------------------------------------------------- /build/composer-php-74.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "driftingly/rector-laravel", 3 | "type": "rector-extension", 4 | "license": "MIT", 5 | "description": "Rector upgrades rules for Laravel Framework", 6 | "require": { 7 | "php": "^7.4 || ^8.0", 8 | "rector/rector": "^2.0" 9 | }, 10 | "autoload": { 11 | "psr-4": { 12 | "RectorLaravel\\": "src" 13 | } 14 | }, 15 | "autoload-dev": { 16 | "classmap": ["stubs"] 17 | }, 18 | "minimum-stability": "dev", 19 | "prefer-stable": true 20 | } 21 | -------------------------------------------------------------------------------- /build/rector-downgrade-php-74.php: -------------------------------------------------------------------------------- 1 | withDowngradeSets(php74: true); 8 | -------------------------------------------------------------------------------- /commands/make-rule.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | $arg) { 12 | if ($i === 0) { 13 | continue; 14 | } // Skip script name 15 | 16 | if ($arg === '--configurable' || $arg === '-c') { 17 | $configurable = true; 18 | } elseif (empty($ruleName) && ! str_starts_with($arg, '-')) { 19 | // If it's not an option, and we don't have a rule name yet, it's the rule name 20 | $ruleName = $arg; 21 | } 22 | } 23 | 24 | $command = new \RectorLaravel\Commands\MakeRuleCommand; 25 | exit($command->execute($ruleName, $configurable)); 26 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "driftingly/rector-laravel", 3 | "type": "rector-extension", 4 | "license": "MIT", 5 | "description": "Rector upgrades rules for Laravel Framework", 6 | "require": { 7 | "php": ">=8.3", 8 | "rector/rector": "^2.0.0", 9 | "webmozart/assert": "^1.11", 10 | "symplify/rule-doc-generator-contracts": "^11.2" 11 | }, 12 | "require-dev": { 13 | "nikic/php-parser": "^5.3", 14 | "phpstan/extension-installer": "^1.3", 15 | "phpstan/phpstan": "^2.0", 16 | "phpstan/phpstan-deprecation-rules": "^2.0", 17 | "phpstan/phpstan-strict-rules": "^2.0", 18 | "phpstan/phpstan-webmozart-assert": "^2.0", 19 | "phpunit/phpunit": "^10.5", 20 | "symplify/rule-doc-generator": "^12.2", 21 | "tightenco/duster": "^3.1", 22 | "nette/utils": "^4.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "RectorLaravel\\": "src" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "RectorLaravel\\Tests\\": "tests", 32 | "RectorLaravel\\Commands\\": "commands" 33 | }, 34 | "classmap": ["stubs"] 35 | }, 36 | "scripts": { 37 | "phpstan": "vendor/bin/phpstan analyse --ansi", 38 | "test": "vendor/bin/phpunit tests", 39 | "lint": "vendor/bin/duster lint", 40 | "fix": "vendor/bin/duster fix", 41 | "rector-dry-run": "vendor/bin/rector process --dry-run --ansi", 42 | "rector": "vendor/bin/rector process --ansi", 43 | "docs": "vendor/bin/rule-doc-generator generate src --output-file docs/rector_rules_overview.md --ansi", 44 | "make:rule": "php commands/make-rule.php" 45 | }, 46 | "minimum-stability": "dev", 47 | "prefer-stable": true, 48 | "config": { 49 | "allow-plugins": { 50 | "rector/extension-installer": true, 51 | "phpstan/extension-installer": true, 52 | "cweagans/composer-patches": false 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 11 | 12 | $rectorConfig 13 | ->ruleWithConfiguration( 14 | ArrayDimFetchToMethodCallRector::class, 15 | [ 16 | new ArrayDimFetchToMethodCall( 17 | new ObjectType('Illuminate\Foundation\Application'), 18 | 'make', 19 | ), 20 | new ArrayDimFetchToMethodCall( 21 | new ObjectType('Illuminate\Contracts\Foundation\Application'), 22 | 'make', 23 | ), 24 | new ArrayDimFetchToMethodCall( 25 | new ObjectType('Illuminate\Config\Repository'), 26 | 'get', 27 | ), 28 | new ArrayDimFetchToMethodCall( 29 | new ObjectType('Illuminate\Contracts\Config\Repository'), 30 | 'make', 31 | ), 32 | new ArrayDimFetchToMethodCall( 33 | new ObjectType('Illuminate\Contracts\Container\Container\Application'), 34 | 'make', 35 | ), 36 | new ArrayDimFetchToMethodCall( 37 | new ObjectType('Illuminate\Contracts\Container\Container'), 38 | 'make', 39 | ), 40 | ], 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /config/sets/laravel-code-quality.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 12 | $rectorConfig->rule(CallOnAppArrayAccessToStandaloneAssignRector::class); 13 | $rectorConfig->rule(ReverseConditionableMethodCallRector::class); 14 | $rectorConfig->rule(ApplyDefaultInsteadOfNullCoalesceRector::class); 15 | }; 16 | -------------------------------------------------------------------------------- /config/sets/laravel-collection.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 12 | $rectorConfig->rule(AvoidNegatedCollectionContainsOrDoesntContainRector::class); 13 | $rectorConfig->rule(AvoidNegatedCollectionFilterOrRejectRector::class); 14 | $rectorConfig->rule(UnaliasCollectionMethodsRector::class); 15 | }; 16 | -------------------------------------------------------------------------------- /config/sets/laravel-container-string-to-fully-qualified-name.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 13 | 14 | $servicesMap = [ 15 | 'events' => 'Illuminate\Contracts\Events\Dispatcher', 16 | 'config' => 'Illuminate\Contracts\Config\Repository', 17 | 'log' => 'Psr\Log\LoggerInterface', 18 | 'router' => 'Illuminate\Routing\Router', 19 | 'url' => 'Illuminate\Contracts\Routing\UrlGenerator', 20 | 'redirect' => 'Illuminate\Routing\Redirector', 21 | 'auth' => 'Illuminate\Contracts\Auth\Factory', 22 | 'cookie' => 'Illuminate\Cookie\CookieJar', 23 | 'db.factory' => 'Illuminate\Database\Connectors\ConnectionFactory', 24 | 'db' => 'Illuminate\Database\ConnectionResolverInterface', 25 | 'db.connection' => 'Illuminate\Database\ConnectionInterface', 26 | 'db.schema' => 'Illuminate\Database\Schema\SQLiteBuilder', 27 | 'db.transactions' => 'Illuminate\Database\DatabaseTransactionsManager', 28 | 'encrypter' => 'Illuminate\Encryption\Encrypter', 29 | 'files' => 'Illuminate\Filesystem\Filesystem', 30 | 'filesystem' => 'Illuminate\Contracts\Filesystem\Factory', 31 | 'session' => 'Illuminate\Session\SessionManager', 32 | 'session.store' => 'Illuminate\Contracts\Session\Session', 33 | 'view' => 'Illuminate\Contracts\View\Factory', 34 | 'blade.compiler' => 'Illuminate\View\Compilers\CompilerInterface', 35 | 'view.engine.resolver' => 'Illuminate\View\Engines\EngineResolver', 36 | 'cache' => 'Illuminate\Contracts\Cache\Factory', 37 | 'cache.store' => 'Illuminate\Cache\Repository', 38 | 'memcached.connector' => 'Illuminate\Cache\MemcachedConnector', 39 | 'queue' => 'Illuminate\Queue\QueueManager', 40 | 'queue.connection' => 'Illuminate\Contracts\Queue\Queue', 41 | 'queue.worker' => 'Illuminate\Queue\Worker', 42 | 'composer' => 'Illuminate\Support\Composer', 43 | 'hash' => 'Illuminate\Hashing\HashManager', 44 | 'hash.driver' => 'Illuminate\Contracts\Hashing\Hasher', 45 | 'mail.manager' => 'Illuminate\Contracts\Mail\Factory', 46 | 'mailer' => 'Illuminate\Mail\Mailer', 47 | 'auth.password' => 'Illuminate\Contracts\Auth\PasswordBrokerFactory', 48 | 'auth.password.broker' => 'Illuminate\Contracts\Auth\PasswordBroker', 49 | 'redis' => 'Illuminate\Contracts\Redis\Factory', 50 | 'translator' => 'Illuminate\Contracts\Translation\Translator', 51 | 'validation.presence' => 'Illuminate\Validation\DatabasePresenceVerifier', 52 | 'validator' => 'Illuminate\Contracts\Validation\Factory', 53 | 'command.tinker' => 'Laravel\Tinker\Console\TinkerCommand', 54 | ]; 55 | 56 | $ruleConfig = array_map( 57 | fn (string $service, string $interface) => new ReplaceServiceContainerCallArg( 58 | $service, 59 | new ClassConstFetch( 60 | new FullyQualified($interface), 61 | 'class' 62 | ) 63 | ), 64 | array_keys($servicesMap), 65 | $servicesMap, 66 | ); 67 | 68 | $rectorConfig->ruleWithConfiguration( 69 | ReplaceServiceContainerCallArgRector::class, 70 | $ruleConfig 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /config/sets/laravel-eloquent-magic-method-to-query-builder.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 10 | $rectorConfig->rule(EloquentMagicMethodToQueryBuilderRector::class); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/laravel-facade-aliases-to-full-names.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 10 | 11 | $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ 12 | 'App' => 'Illuminate\Support\Facades\App', 13 | 'Arr' => 'Illuminate\Support\Arr', 14 | 'Artisan' => 'Illuminate\Support\Facades\Artisan', 15 | 'Auth' => 'Illuminate\Support\Facades\Auth', 16 | 'Blade' => 'Illuminate\Support\Facades\Blade', 17 | 'Broadcast' => 'Illuminate\Support\Facades\Broadcast', 18 | 'Bus' => 'Illuminate\Support\Facades\Bus', 19 | 'Cache' => 'Illuminate\Support\Facades\Cache', 20 | 'Config' => 'Illuminate\Support\Facades\Config', 21 | 'Context' => 'Illuminate\Support\Facades\Context', 22 | 'Cookie' => 'Illuminate\Support\Facades\Cookie', 23 | 'Crypt' => 'Illuminate\Support\Facades\Crypt', 24 | 'Date' => 'Illuminate\Support\Facades\Date', 25 | 'DB' => 'Illuminate\Support\Facades\DB', 26 | 'Eloquent' => 'Illuminate\Database\Eloquent\Model', 27 | 'Event' => 'Illuminate\Support\Facades\Event', 28 | 'File' => 'Illuminate\Support\Facades\File', 29 | 'Gate' => 'Illuminate\Support\Facades\Gate', 30 | 'Hash' => 'Illuminate\Support\Facades\Hash', 31 | 'Http' => 'Illuminate\Support\Facades\Http', 32 | 'Js' => 'Illuminate\Support\Js', 33 | 'Lang' => 'Illuminate\Support\Facades\Lang', 34 | 'Log' => 'Illuminate\Support\Facades\Log', 35 | 'Mail' => 'Illuminate\Support\Facades\Mail', 36 | 'Notification' => 'Illuminate\Support\Facades\Notification', 37 | 'Number' => 'Illuminate\Support\Number', 38 | 'Password' => 'Illuminate\Support\Facades\Password', 39 | 'Process' => 'Illuminate\Support\Facades\Process', 40 | 'Queue' => 'Illuminate\Support\Facades\Queue', 41 | 'RateLimiter' => 'Illuminate\Support\Facades\RateLimiter', 42 | 'Redirect' => 'Illuminate\Support\Facades\Redirect', 43 | 'Request' => 'Illuminate\Support\Facades\Request', 44 | 'Response' => 'Illuminate\Support\Facades\Response', 45 | 'Route' => 'Illuminate\Support\Facades\Route', 46 | 'Schema' => 'Illuminate\Support\Facades\Schema', 47 | 'Session' => 'Illuminate\Support\Facades\Session', 48 | 'Storage' => 'Illuminate\Support\Facades\Storage', 49 | 'Str' => 'Illuminate\Support\Str', 50 | 'URL' => 'Illuminate\Support\Facades\URL', 51 | 'Validator' => 'Illuminate\Support\Facades\Validator', 52 | 'View' => 'Illuminate\Support\Facades\View', 53 | 'Vite' => 'Illuminate\Support\Facades\Vite', 54 | ]); 55 | }; 56 | -------------------------------------------------------------------------------- /config/sets/laravel-if-helpers.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 11 | 12 | $rectorConfig->rule(AbortIfRector::class); 13 | $rectorConfig->rule(ReportIfRector::class); 14 | $rectorConfig->rule(ThrowIfRector::class); 15 | }; 16 | -------------------------------------------------------------------------------- /config/sets/laravel-legacy-factories-to-classes.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 12 | 13 | // https://laravel.com/docs/7.x/database-testing#writing-factories 14 | // https://laravel.com/docs/8.x/database-testing#defining-model-factories 15 | $rectorConfig->rule(FactoryDefinitionRector::class); 16 | 17 | // https://laravel.com/docs/7.x/database-testing#using-factories 18 | // https://laravel.com/docs/8.x/database-testing#creating-models-using-factories 19 | $rectorConfig->rule(FactoryApplyingStatesRector::class); 20 | $rectorConfig->rule(FactoryFuncCallToStaticCallRector::class); 21 | }; 22 | -------------------------------------------------------------------------------- /config/sets/laravel100.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 22 | 23 | // https://github.com/laravel/framework/pull/32856/files 24 | $rectorConfig->rule(UnifyModelDatesWithCastsRector::class); 25 | 26 | // https://github.com/laravel/framework/pull/44784/files 27 | $rectorConfig->rule(DatabaseExpressionCastsToMethodCallRector::class); 28 | $rectorConfig->rule(DatabaseExpressionToStringToMethodCallRector::class); 29 | 30 | // https://github.com/laravel/framework/pull/41136/files 31 | $rectorConfig->rule(ReplaceExpectsMethodsInTestsRector::class); 32 | $rectorConfig->rule(ReplaceAssertTimesSendWithAssertSentTimesRector::class); 33 | $rectorConfig->rule(ReplaceWithoutJobsEventsAndNotificationsWithFacadeFakeRector::class); 34 | 35 | $rectorConfig 36 | ->ruleWithConfiguration(RenamePropertyRector::class, [ 37 | // https://github.com/laravel/laravel/commit/edcbe6de7c3f17070bf0ccaa2e2b785158ae5ceb 38 | new RenameProperty('Illuminate\Foundation\Http\Kernel', 'routeMiddleware', 'middlewareAliases'), 39 | ]); 40 | 41 | $rectorConfig 42 | ->ruleWithConfiguration(RenameMethodRector::class, [ 43 | // https://github.com/laravel/framework/pull/42591/files 44 | new MethodCallRename('Illuminate\Support\Facades\Bus', 'dispatchNow', 'dispatchSync'), 45 | new MethodCallRename('Illuminate\Foundation\Bus\Dispatchable', 'dispatchNow', 'dispatchSync'), 46 | new MethodCallRename('Illuminate\Foundation\Bus\DispatchesJobs', 'dispatchNow', 'dispatchSync'), 47 | ]); 48 | 49 | $rectorConfig 50 | ->ruleWithConfiguration(RenameFunctionRector::class, [ 51 | // https://github.com/laravel/framework/pull/42591/files 52 | 'dispatch_now' => 'dispatch_sync', 53 | ]); 54 | 55 | // https://laravel.com/docs/10.x/upgrade#dispatch-return 56 | $rectorConfig 57 | ->rule(DispatchNonShouldQueueToDispatchSyncRector::class); 58 | }; 59 | -------------------------------------------------------------------------------- /config/sets/laravel110.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 13 | 14 | // https://github.com/laravel/framework/pull/47237 15 | $rectorConfig->rule(ModelCastsPropertyToCastsMethodRector::class); 16 | 17 | // https://github.com/laravel/framework/pull/49634 18 | $rectorConfig->rule(RefactorBlueprintGeometryColumnsRector::class); 19 | 20 | // https://github.com/laravel/framework/pull/52285 21 | $rectorConfig->rule(AssertSeeToAssertSeeHtmlRector::class); 22 | }; 23 | -------------------------------------------------------------------------------- /config/sets/laravel120.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 12 | 13 | // https://github.com/laravel/framework/pull/54628 14 | $rectorConfig->rule(ContainerBindConcreteWithClosureOnlyRector::class); 15 | // https://github.com/laravel/framework/pull/54450 16 | $rectorConfig->rule(ScopeNamedClassMethodToScopeAttributedClassMethodRector::class); 17 | }; 18 | -------------------------------------------------------------------------------- /config/sets/laravel50.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 13 | 14 | // https://stackoverflow.com/a/24949656/1348344 15 | $rectorConfig 16 | ->ruleWithConfiguration(RenameClassRector::class, [ 17 | 'Illuminate\Cache\CacheManager' => 'Illuminate\Contracts\Cache\Repository', 18 | 'Illuminate\Database\Eloquent\SoftDeletingTrait' => 'Illuminate\Database\Eloquent\SoftDeletes', 19 | ]); 20 | 21 | $rectorConfig 22 | ->ruleWithConfiguration( 23 | RenameMethodRector::class, 24 | [new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'links', 'render'), 25 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getFrom', 'firstItem'), 26 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getTo', 'lastItem'), 27 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getPerPage', 'perPage'), 28 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getCurrentPage', 'currentPage'), 29 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getLastPage', 'lastPage'), 30 | new MethodCallRename('Illuminate\Contracts\Pagination\Paginator', 'getTotal', 'total'), 31 | ] 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /config/sets/laravel51.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 11 | $rectorConfig 12 | ->ruleWithConfiguration(RenameClassRector::class, [ 13 | 'Illuminate\Validation\Validator' => 'Illuminate\Contracts\Validation\Validator', 14 | ]); 15 | }; 16 | -------------------------------------------------------------------------------- /config/sets/laravel52.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 13 | $rectorConfig 14 | ->ruleWithConfiguration(RenameClassRector::class, [ 15 | 'Illuminate\Auth\Access\UnauthorizedException' => 'Illuminate\Auth\Access\AuthorizationException', 16 | 'Illuminate\Http\Exception\HttpResponseException' => 'Illuminate\Foundation\Validation\ValidationException', 17 | 'Illuminate\Foundation\Composer' => 'Illuminate\Support\Composer', 18 | ]); 19 | 20 | $rectorConfig 21 | ->ruleWithConfiguration( 22 | StringToClassConstantRector::class, 23 | [new StringToClassConstant('artisan.start', 'Illuminate\Console\Events\ArtisanStarting', 'class'), 24 | new StringToClassConstant('auth.attempting', 'Illuminate\Auth\Events\Attempting', 'class'), 25 | new StringToClassConstant('auth.login', 'Illuminate\Auth\Events\Login', 'class'), 26 | new StringToClassConstant('auth.logout', 'Illuminate\Auth\Events\Logout', 'class'), 27 | new StringToClassConstant('cache.missed', 'Illuminate\Cache\Events\CacheMissed', 'class'), 28 | new StringToClassConstant('cache.hit', 'Illuminate\Cache\Events\CacheHit', 'class'), 29 | new StringToClassConstant('cache.write', 'Illuminate\Cache\Events\KeyWritten', 'class'), 30 | new StringToClassConstant('cache.delete', 'Illuminate\Cache\Events\KeyForgotten', 'class'), 31 | new StringToClassConstant('illuminate.query', 'Illuminate\Database\Events\QueryExecuted', 'class'), 32 | new StringToClassConstant('illuminate.queue.before', 'Illuminate\Queue\Events\JobProcessing', 'class'), 33 | new StringToClassConstant('illuminate.queue.after', 'Illuminate\Queue\Events\JobProcessed', 'class'), 34 | new StringToClassConstant('illuminate.queue.failed', 'Illuminate\Queue\Events\JobFailed', 'class'), 35 | new StringToClassConstant( 36 | 'illuminate.queue.stopping', 37 | 'Illuminate\Queue\Events\WorkerStopping', 38 | 'class' 39 | ), 40 | new StringToClassConstant('mailer.sending', 'Illuminate\Mail\Events\MessageSending', 'class'), 41 | new StringToClassConstant('router.matched', 'Illuminate\Routing\Events\RouteMatched', 'class'), 42 | ] 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /config/sets/laravel53.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 16 | 17 | $rectorConfig 18 | ->ruleWithConfiguration(RemoveTraitUseRector::class, [ 19 | // see https://laravel.com/docs/5.3/upgrade 20 | 'Illuminate\Foundation\Auth\Access\AuthorizesResources', 21 | ]); 22 | 23 | // https://laravel.com/docs/5.3/upgrade#5.2-deprecations 24 | $rectorConfig 25 | ->ruleWithConfiguration(RenameMethodRector::class, [ 26 | new MethodCallRename('Illuminate\Support\Collection', 'lists', 'pluck'), 27 | new MethodCallRename('Illuminate\Database\Query\Builder', 'lists', 'pluck'), 28 | new MethodCallRename('Illuminate\Database\Eloquent\Collection', 'withHidden', 'makeVisible'), 29 | new MethodCallRename('Illuminate\Database\Eloquent\Model', 'withHidden', 'makeVisible'), 30 | ]); 31 | 32 | $rectorConfig 33 | ->ruleWithConfiguration(RemoveInterfacesRector::class, [ 34 | 'Illuminate\Contracts\Bus\SelfHandling', 35 | ]); 36 | 37 | $rectorConfig 38 | ->ruleWithConfiguration(RenameClassRector::class, [ 39 | 'Illuminate\Database\Eloquent\ScopeInterface' => 'Illuminate\Database\Eloquent\Scope', 40 | 'Illuminate\View\Expression' => 'Illuminate\Support\HtmlString', 41 | ]); 42 | 43 | $rectorConfig 44 | ->ruleWithConfiguration(StaticCallToFuncCallRector::class, [ 45 | new StaticCallToFuncCall('Illuminate\Support\Str', 'randomBytes', 'random_bytes'), 46 | new StaticCallToFuncCall('Illuminate\Support\Str', 'equals', 'hash_equals'), 47 | ]); 48 | }; 49 | -------------------------------------------------------------------------------- /config/sets/laravel55.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 16 | $rectorConfig 17 | ->ruleWithConfiguration( 18 | RenameMethodRector::class, 19 | [new MethodCallRename('Illuminate\Console\Command', 'fire', 'handle')] 20 | ); 21 | 22 | $rectorConfig 23 | ->ruleWithConfiguration( 24 | RenamePropertyRector::class, 25 | [new RenameProperty('Illuminate\Database\Eloquent\Concerns\HasEvents', 'events', 'dispatchesEvents'), 26 | new RenameProperty('Illuminate\Database\Eloquent\Relations\Pivot', 'parent', 'pivotParent'), 27 | ] 28 | ); 29 | 30 | $rectorConfig 31 | ->ruleWithConfiguration(RenameClassRector::class, [ 32 | 'Illuminate\Translation\LoaderInterface' => 'Illuminate\Contracts\Translation\Loader', 33 | ]); 34 | }; 35 | -------------------------------------------------------------------------------- /config/sets/laravel56.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 16 | $rectorConfig 17 | ->ruleWithConfiguration(RenameMethodRector::class, [new MethodCallRename( 18 | 'Illuminate\Validation\ValidatesWhenResolvedTrait', 19 | 'validate', 20 | 'validateResolved' 21 | ), 22 | new MethodCallRename( 23 | 'Illuminate\Contracts\Validation\ValidatesWhenResolved', 24 | 'validate', 25 | 'validateResolved' 26 | ), 27 | ]); 28 | 29 | $rectorConfig 30 | ->ruleWithConfiguration( 31 | ChangeMethodVisibilityRector::class, 32 | [new ChangeMethodVisibility('Illuminate\Routing\Router', 'addRoute', Visibility::PUBLIC), 33 | new ChangeMethodVisibility('Illuminate\Contracts\Auth\Access\Gate', 'raw', Visibility::PUBLIC), 34 | new ChangeMethodVisibility('Illuminate\Database\Grammar', 'getDateFormat', Visibility::PUBLIC), 35 | ] 36 | ); 37 | }; 38 | -------------------------------------------------------------------------------- /config/sets/laravel57.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 23 | $rectorConfig 24 | ->ruleWithConfiguration( 25 | ChangeMethodVisibilityRector::class, 26 | [new ChangeMethodVisibility('Illuminate\Routing\Router', 'addRoute', Visibility::PUBLIC), 27 | new ChangeMethodVisibility('Illuminate\Contracts\Auth\Access\Gate', 'raw', Visibility::PUBLIC), 28 | ] 29 | ); 30 | $rectorConfig 31 | ->ruleWithConfiguration( 32 | ArgumentAdderRector::class, 33 | [new ArgumentAdder('Illuminate\Auth\Middleware\Authenticate', 'authenticate', 0, 'request'), 34 | new ArgumentAdder( 35 | 'Illuminate\Foundation\Auth\ResetsPasswords', 36 | 'sendResetResponse', 37 | 0, 38 | 'request', 39 | null, 40 | new ObjectType('Illuminate\Http\Illuminate\Http') 41 | ), 42 | new ArgumentAdder( 43 | 'Illuminate\Foundation\Auth\SendsPasswordResetEmails', 44 | 'sendResetLinkResponse', 45 | 0, 46 | 'request', 47 | null, 48 | new ObjectType('Illuminate\Http\Illuminate\Http') 49 | ), 50 | new ArgumentAdder('Illuminate\Database\ConnectionInterface', 'select', 2, 'useReadPdo', true), 51 | new ArgumentAdder('Illuminate\Database\ConnectionInterface', 'selectOne', 2, 'useReadPdo', true), 52 | ] 53 | ); 54 | $rectorConfig->rule(Redirect301ToPermanentRedirectRector::class); 55 | $rectorConfig 56 | ->ruleWithConfiguration( 57 | ArgumentRemoverRector::class, 58 | [new ArgumentRemover('Illuminate\Foundation\Application', 'register', 1, [ 59 | 'name' => 'options', 60 | ]), 61 | ] 62 | ); 63 | $rectorConfig->rule(AddParentBootToModelClassMethodRector::class); 64 | $rectorConfig->rule(ChangeQueryWhereDateValueWithCarbonRector::class); 65 | $rectorConfig->rule(AddMockConsoleOutputFalseToConsoleTestsRector::class); 66 | $rectorConfig->rule(AddGuardToLoginEventRector::class); 67 | }; 68 | -------------------------------------------------------------------------------- /config/sets/laravel58.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/laravel-array-str-functions-to-static-call.php'); 18 | 19 | $rectorConfig 20 | ->ruleWithConfiguration( 21 | AddReturnTypeDeclarationRector::class, 22 | [new AddReturnTypeDeclaration('Illuminate\Contracts\Cache\Repository', 'put', new BooleanType), 23 | new AddReturnTypeDeclaration('Illuminate\Contracts\Cache\Repository', 'forever', new BooleanType), 24 | new AddReturnTypeDeclaration('Illuminate\Contracts\Cache\Store', 'put', new BooleanType), 25 | new AddReturnTypeDeclaration('Illuminate\Contracts\Cache\Store', 'putMany', new BooleanType), 26 | new AddReturnTypeDeclaration('Illuminate\Contracts\Cache\Store', 'forever', new BooleanType), 27 | ] 28 | ); 29 | $rectorConfig 30 | ->ruleWithConfiguration( 31 | RenamePropertyRector::class, 32 | [new RenameProperty('Illuminate\Routing\UrlGenerator', 'cachedSchema', 'cachedScheme')] 33 | ); 34 | $rectorConfig->rule(PropertyDeferToDeferrableProviderToRector::class); 35 | }; 36 | -------------------------------------------------------------------------------- /config/sets/laravel60.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 22 | 23 | $rectorConfig 24 | ->ruleWithConfiguration(RenameMethodRector::class, [ 25 | new MethodCallRename( 26 | 'Illuminate\Auth\Access\Gate', 27 | // https://github.com/laravel/framework/commit/69de466ddc25966a0f6551f48acab1afa7bb9424 28 | 'access', 29 | 'inspect' 30 | ), 31 | new MethodCallRename( 32 | 'Illuminate\Support\Facades\Lang', 33 | // https://github.com/laravel/framework/commit/efbe23c4116f86846ad6edc0d95cd56f4175a446 34 | 'trans', 35 | 'get' 36 | ), 37 | new MethodCallRename('Illuminate\Support\Facades\Lang', 'transChoice', 'choice'), 38 | new MethodCallRename( 39 | 'Illuminate\Translation\Translator', 40 | // https://github.com/laravel/framework/commit/697b898a1c89881c91af83ecc4493fa681e2aa38 41 | 'getFromJson', 42 | 'get' 43 | ), 44 | ]); 45 | 46 | $rectorConfig 47 | ->ruleWithConfiguration(RenameStaticMethodRector::class, [ 48 | // https://github.com/laravel/framework/commit/55785d3514a8149d4858acef40c56a31b6b2ccd1 49 | new RenameStaticMethod( 50 | 'Illuminate\Support\Facades\Input', 51 | 'get', 52 | 'Illuminate\Support\Facades\Request', 53 | 'input' 54 | ), 55 | ]); 56 | 57 | $rectorConfig 58 | ->ruleWithConfiguration(RenameClassRector::class, [ 59 | 'Illuminate\Support\Facades\Input' => 'Illuminate\Support\Facades\Request', 60 | ]); 61 | 62 | $rectorConfig 63 | ->ruleWithConfiguration(ChangeMethodVisibilityRector::class, [ 64 | new ChangeMethodVisibility( 65 | 'Illuminate\Foundation\Http\FormRequest', 66 | 'validationData', 67 | Visibility::PUBLIC 68 | ), 69 | ]); 70 | 71 | $rectorConfig 72 | ->ruleWithConfiguration(ArgumentAdderRector::class, [ // https://github.com/laravel/framework/commit/6c1e014943a508afb2c10869c3175f7783a004e1 73 | new ArgumentAdder('Illuminate\Database\Capsule\Manager', 'table', 1, 'as', null), 74 | ]); 75 | }; 76 | -------------------------------------------------------------------------------- /config/sets/laravel70.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 18 | 19 | // https://github.com/laravel/framework/pull/30610/files 20 | $rectorConfig 21 | ->ruleWithConfiguration(AddParamTypeDeclarationRector::class, [ 22 | new AddParamTypeDeclaration( 23 | 'Illuminate\Contracts\Debug\ExceptionHandler', 24 | 'report', 25 | 0, 26 | new ObjectType('Throwable') 27 | ), 28 | new AddParamTypeDeclaration( 29 | 'Illuminate\Contracts\Debug\ExceptionHandler', 30 | 'shouldReport', 31 | 0, 32 | new ObjectType('Throwable') 33 | ), 34 | new AddParamTypeDeclaration( 35 | 'Illuminate\Contracts\Debug\ExceptionHandler', 36 | 'render', 37 | 1, 38 | new ObjectType('Throwable') 39 | ), 40 | new AddParamTypeDeclaration( 41 | 'Illuminate\Contracts\Debug\ExceptionHandler', 42 | 'renderForConsole', 43 | 1, 44 | new ObjectType('Throwable') 45 | ), 46 | ]); 47 | 48 | // https://github.com/laravel/framework/pull/30471/files 49 | $rectorConfig 50 | ->ruleWithConfiguration(ArgumentAdderRector::class, [new ArgumentAdder( 51 | 'Illuminate\Contracts\Routing\UrlRoutable', 52 | 'resolveRouteBinding', 53 | 1, 54 | 'field', 55 | null 56 | ), 57 | ]); 58 | 59 | $rectorConfig 60 | ->ruleWithConfiguration(RenameMethodRector::class, [ // https://github.com/laravel/framework/commit/aece7d78f3d28b2cdb63185dcc4a9b6092841310 61 | new MethodCallRename('Illuminate\Support\Facades\Blade', 'component', 'aliasComponent'), 62 | // https://github.com/laravel/framework/pull/31463/files 63 | new MethodCallRename( 64 | 'Illuminate\Database\Eloquent\Concerns\HidesAttributes', 65 | 'addHidden', 66 | 'makeHidden' 67 | ), 68 | // https://github.com/laravel/framework/pull/30348/files 69 | new MethodCallRename( 70 | 'Illuminate\Database\Eloquent\Concerns\HidesAttributes', 71 | 'addVisible', 72 | 'makeVisible' 73 | ), 74 | ]); 75 | $rectorConfig 76 | ->ruleWithConfiguration(RenameClassRector::class, [ 77 | // https://github.com/laravel/framework/pull/30619/files 78 | 'Illuminate\Http\Resources\Json\Resource' => 'Illuminate\Http\Resources\Json\JsonResource', 79 | // https://github.com/laravel/framework/pull/31050/files 80 | 'Illuminate\Foundation\Testing\TestResponse' => 'Illuminate\Testing\TestResponse', 81 | 'Illuminate\Foundation\Testing\Assert' => 'Illuminate\Testing\Assert', 82 | ]); 83 | }; 84 | -------------------------------------------------------------------------------- /config/sets/laravel80.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../config.php'); 21 | 22 | // https://github.com/laravel/framework/commit/4d228d6e9dbcbd4d97c45665980d8b8c685b27e6 23 | $rectorConfig 24 | ->ruleWithConfiguration(ArgumentAdderRector::class, [new ArgumentAdderWithoutDefaultValue( 25 | 'Illuminate\Contracts\Database\Eloquent\Castable', 26 | 'castUsing', 27 | 0, 28 | 'arguments', 29 | new ArrayType(new MixedType, new MixedType) 30 | ), 31 | ]); 32 | 33 | // https://github.com/laravel/framework/commit/46084d946cdcd1ae1f32fc87a4f1cc9e3a5bccf6 34 | $rectorConfig 35 | ->ruleWithConfiguration( 36 | AddArgumentDefaultValueRector::class, 37 | [new AddArgumentDefaultValue('Illuminate\Contracts\Events\Dispatcher', 'listen', 1, null)] 38 | ); 39 | 40 | // https://github.com/laravel/framework/commit/f1289515b27e93248c09f04e3011bb7ce21b2737 41 | $rectorConfig->rule(AddParentRegisterToEventServiceProviderRector::class); 42 | 43 | $rectorConfig 44 | ->ruleWithConfiguration(RenamePropertyRector::class, [ // https://github.com/laravel/framework/pull/32092/files 45 | new RenameProperty('Illuminate\Support\Manager', 'app', 'container'), 46 | // https://github.com/laravel/framework/commit/4656c2cf012ac62739ab5ea2bce006e1e9fe8f09 47 | new RenameProperty('Illuminate\Contracts\Queue\ShouldQueue', 'retryAfter', 'backoff'), 48 | // https://github.com/laravel/framework/commit/12c35e57c0a6da96f36ad77f88f083e96f927205 49 | new RenameProperty('Illuminate\Contracts\Queue\ShouldQueue', 'timeoutAt', 'retryUntil'), 50 | ]); 51 | 52 | $rectorConfig 53 | ->ruleWithConfiguration(RenameMethodRector::class, [ // https://github.com/laravel/framework/pull/32092/files 54 | new MethodCallRename('Illuminate\Mail\PendingMail', 'sendNow', 'send'), 55 | // https://github.com/laravel/framework/commit/4656c2cf012ac62739ab5ea2bce006e1e9fe8f09 56 | new MethodCallRename('Illuminate\Contracts\Queue\ShouldQueue', 'retryAfter', 'backoff'), 57 | // https://github.com/laravel/framework/commit/12c35e57c0a6da96f36ad77f88f083e96f927205 58 | new MethodCallRename('Illuminate\Contracts\Queue\ShouldQueue', 'timeoutAt', 'retryUntil'), 59 | // https://github.com/laravel/framework/commit/f9374fa5fb0450721fb2f90e96adef9d409b112c 60 | new MethodCallRename('Illuminate\Testing\TestResponse', 'decodeResponseJson', 'json'), 61 | ]); 62 | }; 63 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-100.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_100, LaravelLevelSetList::UP_TO_LARAVEL_90]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-110.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_110, LaravelLevelSetList::UP_TO_LARAVEL_100]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-120.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_120, LaravelLevelSetList::UP_TO_LARAVEL_110]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-51.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_50, LaravelSetList::LARAVEL_51]); 10 | }; 11 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-52.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_52, LaravelLevelSetList::UP_TO_LARAVEL_51]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-53.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_53, LaravelLevelSetList::UP_TO_LARAVEL_52]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-54.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_54, LaravelLevelSetList::UP_TO_LARAVEL_53]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-55.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_55, LaravelLevelSetList::UP_TO_LARAVEL_54]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-56.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_56, LaravelLevelSetList::UP_TO_LARAVEL_55]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-57.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_57, LaravelLevelSetList::UP_TO_LARAVEL_56]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-58.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_58, LaravelLevelSetList::UP_TO_LARAVEL_57]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-60.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_60, LaravelLevelSetList::UP_TO_LARAVEL_58]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-70.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_70, LaravelLevelSetList::UP_TO_LARAVEL_60]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-80.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_80, LaravelLevelSetList::UP_TO_LARAVEL_70]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/level/up-to-laravel-90.php: -------------------------------------------------------------------------------- 1 | sets([LaravelSetList::LARAVEL_90, LaravelLevelSetList::UP_TO_LARAVEL_80]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/packages/cashier/cashier-13.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../../../config.php'); 13 | 14 | $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [ 15 | new MethodCallRename('Laravel\Cashier\Billable', 'subscribedToPlan', 'subscribedToPrice'), 16 | new MethodCallRename('Laravel\Cashier\Billable', 'onPlan', 'onPrice'), 17 | new MethodCallRename('Laravel\Cashier\Billable', 'planTaxRates', 'priceTaxRates'), 18 | new MethodCallRename('Laravel\Cashier\SubscriptionBuilder', 'plan', 'price'), 19 | new MethodCallRename('Laravel\Cashier\SubscriptionBuilder', 'meteredPlan', 'meteredPrice'), 20 | new MethodCallRename('Laravel\Cashier\Subscription', 'hasMultiplePlans', 'hasMultiplePrices'), 21 | new MethodCallRename('Laravel\Cashier\Subscription', 'hasSinglePlan', 'hasSinglePrice'), 22 | new MethodCallRename('Laravel\Cashier\Subscription', 'hasPlan', 'hasPrice'), 23 | new MethodCallRename('Laravel\Cashier\Subscription', 'addPlan', 'addPrice'), 24 | new MethodCallRename('Laravel\Cashier\Subscription', 'addPlanAndInvoice', 'addPriceAndInvoice'), 25 | new MethodCallRename('Laravel\Cashier\Subscription', 'removePlan', 'removePrice'), 26 | ]); 27 | 28 | $rectorConfig->rule(CashierStripeOptionsToStripeRector::class); 29 | }; 30 | -------------------------------------------------------------------------------- /config/sets/packages/cashier/cashier-14.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../../../config.php'); 12 | 13 | $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [ 14 | new MethodCallRename('Laravel\Cashier\Billable', 'removePaymentMethod', 'deletePaymentMethod'), 15 | new MethodCallRename('Laravel\Cashier\Payment', 'isCancelled', 'isCanceled'), 16 | new MethodCallRename('Laravel\Cashier\Subscription', 'cancelled', 'canceled'), 17 | new MethodCallRename('Laravel\Cashier\Subscription', 'markAsCancelled', 'markAsCanceled'), 18 | ]); 19 | }; 20 | -------------------------------------------------------------------------------- /config/sets/packages/cashier/level/up-to-cashier-13.php: -------------------------------------------------------------------------------- 1 | sets([CashierSetList::LARAVEL_CASHIER_130]); 10 | }; 11 | -------------------------------------------------------------------------------- /config/sets/packages/cashier/level/up-to-cashier-14.php: -------------------------------------------------------------------------------- 1 | sets([CashierSetList::LARAVEL_CASHIER_140, CashierLevelSetList::UP_TO_LARAVEL_CASHIER_130]); 11 | }; 12 | -------------------------------------------------------------------------------- /config/sets/packages/livewire/level/up-to-livewire-30.php: -------------------------------------------------------------------------------- 1 | sets([LivewireSetList::LIVEWIRE_30]); 10 | }; 11 | -------------------------------------------------------------------------------- /config/sets/packages/livewire/livewire-30.php: -------------------------------------------------------------------------------- 1 | import(__DIR__ . '/../../../config.php'); 13 | 14 | $rectorConfig->rule(LivewireComponentQueryStringToUrlAttributeRector::class); 15 | $rectorConfig->rule(LivewireComponentComputedMethodToComputedAttributeRector::class); 16 | 17 | $rectorConfig->ruleWithConfiguration(RenameAttributeRector::class, [ 18 | new RenameAttribute('Livewire\Attributes\Rule', 'Livewire\Attributes\Validate'), 19 | ]); 20 | }; 21 | -------------------------------------------------------------------------------- /rector.php: -------------------------------------------------------------------------------- 1 | withImportNames() 10 | ->withPaths([ 11 | __DIR__ . '/src', 12 | __DIR__ . '/tests', 13 | __DIR__ . '/config', 14 | ]) 15 | ->withSkip([ 16 | // for tests 17 | '*/Source/*', 18 | '*/Fixture/*', 19 | 20 | // skip for handle scoped, like in the rector-src as well 21 | // @see https://github.com/rectorphp/rector-src/blob/7f73cf017214257c170d34db3af7283eaeeab657/rector.php#L71 22 | StringClassNameToClassConstantRector::class, 23 | ]) 24 | ->withPhpSets() 25 | ->withPreparedSets(deadCode: true, codeQuality: true, naming: true); 26 | -------------------------------------------------------------------------------- /src/AbstractRector.php: -------------------------------------------------------------------------------- 1 | version = $version; 19 | 20 | return $this; 21 | } 22 | 23 | public function setApplicationClass(string $applicationClass): static 24 | { 25 | $this->applicationClass = $applicationClass; 26 | 27 | return $this; 28 | } 29 | 30 | public function getApplicationClass(): string 31 | { 32 | return $this->applicationClass; 33 | } 34 | 35 | /** 36 | * @param '>='|'='|'<=' $comparison 37 | */ 38 | public function isVersion(string $comparison, string $version): bool 39 | { 40 | return version_compare($this->getVersion(), $version, $comparison); 41 | } 42 | 43 | public function getVersion(): string 44 | { 45 | if ($this->version !== null) { 46 | return $this->version; 47 | } 48 | 49 | $reflectionClassConstant = new ReflectionClassConstant($this->applicationClass, 'VERSION'); 50 | 51 | if (! is_string($version = $reflectionClassConstant->getValue())) { 52 | throw new RuntimeException('expected VERSION to be a string, got ' . gettype($version)); 53 | } 54 | 55 | return $version; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/FacadeAssertionAnalyzer.php: -------------------------------------------------------------------------------- 1 | nodeTypeResolver->isObjectType($staticCall->class, new ObjectType('Illuminate\Support\Facades\Bus')) 21 | && $this->nodeNameResolver->isName($staticCall->name, 'assertDispatched') => true, 22 | $this->nodeTypeResolver->isObjectType($staticCall->class, new ObjectType('Illuminate\Support\Facades\Event')) 23 | && $this->nodeNameResolver->isName($staticCall->name, 'assertDispatched') => true, 24 | default => false, 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/LaravelServiceAnalyzer.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public array $services = [ 20 | 'db' => 'Illuminate\Database\DatabaseManager', 21 | ]; 22 | 23 | public function __construct( 24 | private readonly NodeTypeResolver $nodeTypeResolver, 25 | private readonly NodeNameResolver $nodeNameResolver, 26 | private readonly ReflectionProvider $reflectionProvider, 27 | ) {} 28 | 29 | /** 30 | * @param array $services 31 | */ 32 | public function defineServices(array $services, bool $merge = false): static 33 | { 34 | $this->services = $merge ? array_merge($this->services, $services) : $services; 35 | 36 | return $this; 37 | } 38 | 39 | public function getFacadeOrigin(StaticCall $staticCall): ?ObjectType 40 | { 41 | $classType = $this->nodeTypeResolver->getType($staticCall->class); 42 | $className = $this->nodeNameResolver->getName($staticCall->class); 43 | if (! is_string($className)) { 44 | return null; 45 | } 46 | 47 | if ($classType->hasMethod('getFacadeAccessor')->no()) { 48 | return null; 49 | } 50 | 51 | $reflectionMethod = new ReflectionMethod($className, 'getFacadeAccessor'); 52 | 53 | if (! $reflectionMethod->isStatic() || $reflectionMethod->getNumberOfParameters() > 0) { 54 | return null; 55 | } 56 | $reflectionMethod->setAccessible(true); 57 | $origin = $reflectionMethod->invoke(null); 58 | if (! is_string($origin)) { 59 | return null; 60 | } 61 | 62 | if ($this->reflectionProvider->hasClass($origin)) { 63 | return new ObjectType($origin); 64 | } 65 | 66 | $service = $this->resolveServiceToClass($origin); 67 | 68 | if ($service === null) { 69 | return null; 70 | } 71 | 72 | return new ObjectType($service); 73 | } 74 | 75 | public function isMatchingCall(MethodCall|StaticCall $node, ObjectType $objectType, string $method): bool 76 | { 77 | if (! $this->nodeNameResolver->isName($node->name, $method)) { 78 | return false; 79 | } 80 | 81 | if ($node instanceof StaticCall && $this->isFacadeCall($node)) { 82 | $facadeOriginObjectType = $this->getFacadeOrigin($node); 83 | 84 | if (! $facadeOriginObjectType instanceof Type || $facadeOriginObjectType->isObject()->no()) { 85 | return false; 86 | } 87 | 88 | return $objectType->isSuperTypeOf($facadeOriginObjectType)->yes(); 89 | } elseif ($node instanceof StaticCall) { 90 | return false; 91 | } 92 | 93 | return $this->nodeTypeResolver->isObjectType($node->var, $objectType); 94 | } 95 | 96 | public function isFacadeCall(StaticCall $staticCall): bool 97 | { 98 | return $this->nodeTypeResolver->isObjectType( 99 | $staticCall->class, 100 | new ObjectType('Illuminate\Support\Facades\Facade') 101 | ); 102 | } 103 | 104 | /** 105 | * @return class-string|null 106 | */ 107 | protected function resolveServiceToClass(string $service): ?string 108 | { 109 | return $this->services[$service] ?? null; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/LumenRouteRegisteringMethodAnalyzer.php: -------------------------------------------------------------------------------- 1 | nodeTypeResolver->isObjectType($methodCall->var, new ObjectType('Laravel\Lumen\Routing\Router')); 24 | } 25 | 26 | public function isRoutesRegisterGroup(Identifier|Expr $name): bool 27 | { 28 | return $this->nodeNameResolver->isName($name, 'group'); 29 | } 30 | 31 | public function isRoutesRegisterRoute(Identifier|Expr $name): bool 32 | { 33 | return $this->nodeNameResolver->isNames($name, ['delete', 'get', 'options', 'patch', 'post', 'put']); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/ModelAnalyzer.php: -------------------------------------------------------------------------------- 1 | $class 20 | * 21 | * @throws Exception 22 | */ 23 | public function getTable(string $class): ?string 24 | { 25 | $classReflection = $this->getClass($class); 26 | 27 | /** @var Model $instance */ 28 | $instance = $classReflection->getNativeReflection()->newInstanceWithoutConstructor(); 29 | $table = $instance->getTable(); 30 | if (! is_string($table)) { 31 | return null; 32 | } 33 | 34 | return $table; 35 | } 36 | 37 | /** 38 | * @param class-string $class 39 | */ 40 | public function getPrimaryKey(string $class): ?string 41 | { 42 | $classReflection = $this->getClass($class); 43 | 44 | /** @var Model $instance */ 45 | $instance = $classReflection->getNativeReflection()->newInstanceWithoutConstructor(); 46 | $keyName = $instance->getKeyName(); 47 | if (! is_string($keyName)) { 48 | return null; 49 | } 50 | 51 | return $keyName; 52 | } 53 | 54 | /** 55 | * @param class-string $class 56 | * 57 | * @throws Exception 58 | */ 59 | private function getClass(string $class): ClassReflection 60 | { 61 | if (! $this->reflectionProvider->hasClass($class)) { 62 | throw new Exception('Class not found'); 63 | } 64 | 65 | $classReflection = $this->reflectionProvider->getClass($class); 66 | 67 | if (! $classReflection->isClass()) { 68 | throw new Exception('Class is not class'); 69 | } 70 | 71 | if (! $classReflection->isSubclassOfClass($this->reflectionProvider->getClass(Model::class))) { 72 | throw new Exception('Class is not subclass of Model'); 73 | } 74 | 75 | return $classReflection; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/QueryBuilderAnalyzer.php: -------------------------------------------------------------------------------- 1 | nodeNameResolver->isName($node->name, $method)) { 32 | return false; 33 | } 34 | 35 | if ($node instanceof StaticCall) { 36 | return $this->isProxyCall($node); 37 | } 38 | 39 | return $this->nodeTypeResolver->isObjectType($node->var, self::queryBuilderType()); 40 | } 41 | 42 | public function isProxyCall(StaticCall $staticCall): bool 43 | { 44 | if (! $this->nodeTypeResolver->isObjectType($staticCall->class, self::modelType())) { 45 | return false; 46 | } 47 | 48 | $methodName = $this->nodeNameResolver->getName($staticCall->name); 49 | if (! is_string($methodName)) { 50 | return false; 51 | } 52 | 53 | $classType = $this->nodeTypeResolver->getType($staticCall->class); 54 | 55 | if ($classType->isObject()->no()) { 56 | return false; 57 | } 58 | 59 | /** @phpstan-ignore method.notFound */ 60 | $reflectionClass = $classType->getClassReflection(); 61 | 62 | /** @phpstan-ignore phpstanApi.instanceofAssumption */ 63 | if (! $reflectionClass instanceof ClassReflection) { 64 | return false; 65 | } 66 | 67 | return ! $reflectionClass->hasNativeMethod($methodName); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/NodeAnalyzer/StaticCallAnalyzer.php: -------------------------------------------------------------------------------- 1 | class instanceof Expr) { 25 | return false; 26 | } 27 | 28 | if (! $this->nodeNameResolver->isName($node->class, 'parent')) { 29 | return false; 30 | } 31 | 32 | if ($node->name instanceof Expr) { 33 | return false; 34 | } 35 | 36 | return $this->nodeNameResolver->isName($node->name, $desiredMethodName); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/NodeFactory/AppAssignFactory.php: -------------------------------------------------------------------------------- 1 | getVariableName()); 27 | $assign = new Assign($variable, $expr); 28 | $expression = new Expression($assign); 29 | 30 | $this->decorateWithVarAnnotation($expression, $serviceNameTypeAndVariableName); 31 | 32 | return $expression; 33 | } 34 | 35 | private function decorateWithVarAnnotation( 36 | Expression $expression, 37 | ServiceNameTypeAndVariableName $serviceNameTypeAndVariableName 38 | ): void { 39 | $phpDocInfo = $this->phpDocInfoFactory->createEmpty($expression); 40 | 41 | $fullyQualifiedIdentifierTypeNode = new FullyQualifiedIdentifierTypeNode( 42 | $serviceNameTypeAndVariableName->getType() 43 | ); 44 | 45 | $varTagValueNode = new VarTagValueNode( 46 | $fullyQualifiedIdentifierTypeNode, 47 | '$' . $serviceNameTypeAndVariableName->getVariableName(), 48 | '' 49 | ); 50 | 51 | $phpDocInfo->addTagValueNode($varTagValueNode); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NodeFactory/DispatchableTestsMethodsFactory.php: -------------------------------------------------------------------------------- 1 | , ClassConstFetch|String_> $items 18 | */ 19 | public function makeFacadeFakeCall(array $items, string $facade): StaticCall 20 | { 21 | return new StaticCall( 22 | new FullyQualified($facade), 23 | 'fake', 24 | [new Arg(new Array_(array_map(fn (String_|ClassConstFetch $item): ArrayItem => new ArrayItem($item), $items)))] 25 | ); 26 | } 27 | 28 | /** 29 | * @param array, ClassConstFetch|String_> $items 30 | * @return Expression[] 31 | */ 32 | public function assertStatements(array $items, string $facade): array 33 | { 34 | return array_map(fn (String_|ClassConstFetch $item): Expression => new Expression(new StaticCall( 35 | new FullyQualified($facade), 36 | 'assertDispatched', 37 | [new Arg($item)], 38 | )), $items); 39 | } 40 | 41 | /** 42 | * @param array, ClassConstFetch|String_> $items 43 | * @return Expression[] 44 | */ 45 | public function assertNotStatements(array $items, string $facade): array 46 | { 47 | return array_map(fn (String_|ClassConstFetch $item): Expression => new Expression(new StaticCall( 48 | new FullyQualified($facade), 49 | 'assertNotDispatched', 50 | [new Arg($item)], 51 | )), $items); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/NodeFactory/RouterRegisterNodeAnalyzer.php: -------------------------------------------------------------------------------- 1 | isRegisterName($node->name)) { 29 | return false; 30 | } 31 | 32 | if ($node instanceof MethodCall && $this->nodeTypeResolver->isObjectTypes( 33 | $node->var, 34 | [new ObjectType('Illuminate\Routing\Router'), new ObjectType('Illuminate\Routing\RouteRegistrar')] 35 | )) { 36 | return true; 37 | } 38 | 39 | return $node instanceof StaticCall && $this->nodeNameResolver->isNames( 40 | $node->class, 41 | ['Illuminate\Support\Facades\Route', 'Route'] 42 | ); 43 | } 44 | 45 | public function isRegisterName(Identifier|Expr $name): bool 46 | { 47 | if ($this->isRegisterAnyVerb($name)) { 48 | return true; 49 | } 50 | 51 | if ($this->isRegisterMultipleVerbs($name)) { 52 | return true; 53 | } 54 | 55 | if ($this->isRegisterAllVerbs($name)) { 56 | return true; 57 | } 58 | 59 | return $this->isRegisterFallback($name); 60 | } 61 | 62 | public function isRegisterMultipleVerbs(Identifier|Expr $name): bool 63 | { 64 | return $this->nodeNameResolver->isName($name, 'match'); 65 | } 66 | 67 | public function isRegisterAllVerbs(Identifier|Expr $name): bool 68 | { 69 | return $this->nodeNameResolver->isName($name, 'any'); 70 | } 71 | 72 | public function isRegisterAnyVerb(Identifier|Expr $name): bool 73 | { 74 | return $this->nodeNameResolver->isNames($name, ['delete', 'get', 'options', 'patch', 'post', 'put']); 75 | } 76 | 77 | public function isRegisterFallback(Identifier|Expr $name): bool 78 | { 79 | return $this->nodeNameResolver->isName($name, 'fallback'); 80 | } 81 | 82 | public function isGroup(Identifier|Expr $name): bool 83 | { 84 | return $this->nodeNameResolver->isName($name, 'group'); 85 | } 86 | 87 | public function getGroupNamespace(MethodCall|StaticCall $call): string|null|false 88 | { 89 | if (! isset($call->args[0]) || ! $call->args[0] instanceof Arg) { 90 | return null; 91 | } 92 | 93 | $firstArg = $call->args[0]->value; 94 | if (! $firstArg instanceof Array_) { 95 | return null; 96 | } 97 | 98 | foreach ($firstArg->items as $item) { 99 | if (! $item instanceof ArrayItem) { 100 | continue; 101 | } 102 | 103 | if ($item->key instanceof String_ && $item->key->value === 'namespace') { 104 | 105 | if ($item->value instanceof String_) { 106 | return $item->value->value; 107 | } 108 | 109 | // if we can't find the namespace value we specify it exists but is 110 | // unreadable with false 111 | return false; 112 | } 113 | } 114 | 115 | return null; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Rector/ArrayDimFetch/EnvVariableToEnvHelperRector.php: -------------------------------------------------------------------------------- 1 | isName($node->var, '_ENV')) { 44 | return null; 45 | } 46 | 47 | if ($node->dim === null) { 48 | return null; 49 | } 50 | 51 | return $this->nodeFactory->createStaticCall('Illuminate\Support\Env', 'get', [ 52 | new Arg($node->dim), 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Rector/ArrayDimFetch/ServerVariableToRequestFacadeRector.php: -------------------------------------------------------------------------------- 1 | isName($node->var, '_SERVER')) { 44 | return null; 45 | } 46 | 47 | if ($node->dim === null) { 48 | return null; 49 | } 50 | 51 | return $this->nodeFactory->createStaticCall('Illuminate\Support\Facades\Request', 'server', [ 52 | new Arg($node->dim), 53 | ]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Rector/BooleanNot/AvoidNegatedCollectionContainsOrDoesntContainRector.php: -------------------------------------------------------------------------------- 1 | contains(fn (?int $number): bool => is_null($number)); 32 | ! $collection->doesntContain(fn (?int $number) => $number > 0); 33 | CODE_SAMPLE 34 | , 35 | <<<'CODE_SAMPLE' 36 | use Illuminate\Support\Collection; 37 | 38 | $collection = new Collection([0, 1, null, -1]); 39 | $collection->doesntContain(fn (?int $number): bool => is_null($number)); 40 | $collection->contains(fn (?int $number) => $number > 0); 41 | CODE_SAMPLE 42 | ), 43 | ] 44 | ); 45 | } 46 | 47 | /** 48 | * @return array> 49 | */ 50 | public function getNodeTypes(): array 51 | { 52 | return [BooleanNot::class]; 53 | } 54 | 55 | /** 56 | * @param BooleanNot $node 57 | */ 58 | public function refactor(Node $node): ?Node 59 | { 60 | return $this->updateBooleanNot($node); 61 | } 62 | 63 | private function updateBooleanNot(BooleanNot $booleanNot): ?MethodCall 64 | { 65 | $expr = $booleanNot->expr; 66 | if (! $expr instanceof MethodCall) { 67 | return null; 68 | } 69 | 70 | if (! $this->isObjectType($expr->var, new ObjectType('Illuminate\Support\Enumerable'))) { 71 | return null; 72 | } 73 | 74 | $name = $expr->name; 75 | if ($this->isName($name, 'contains')) { 76 | $replacement = 'doesntContain'; 77 | } elseif ($this->isName($name, 'doesntContain')) { 78 | $replacement = 'contains'; 79 | } else { 80 | return null; 81 | } 82 | 83 | $expr->name = new Identifier($replacement); 84 | 85 | return $expr; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Rector/Cast/DatabaseExpressionCastsToMethodCallRector.php: -------------------------------------------------------------------------------- 1 | getValue(DB::connection()->getQueryGrammar()); 33 | CODE_SAMPLE 34 | ), 35 | ]); 36 | } 37 | 38 | /** 39 | * @return array> 40 | */ 41 | public function getNodeTypes(): array 42 | { 43 | return [String_::class]; 44 | } 45 | 46 | /** 47 | * @param String_ $node 48 | */ 49 | public function refactor(Node $node): ?Node 50 | { 51 | if (! $node->expr instanceof StaticCall) { 52 | return null; 53 | } 54 | 55 | if (! $this->isName($node->expr->class, 'Illuminate\Support\Facades\DB')) { 56 | return null; 57 | } 58 | 59 | if (! $this->isName($node->expr->name, 'raw')) { 60 | return null; 61 | } 62 | 63 | return $this->nodeFactory->createMethodCall($node->expr, 'getValue', [ 64 | $this->nodeFactory->createMethodCall( 65 | $this->nodeFactory->createStaticCall('Illuminate\Support\Facades\DB', 'connection'), 66 | 'getQueryGrammar' 67 | ), 68 | ]); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Rector/ClassMethod/ScopeNamedClassMethodToScopeAttributedClassMethodRector.php: -------------------------------------------------------------------------------- 1 | where('active', 1); 44 | } 45 | } 46 | CODE_SAMPLE, 47 | <<<'CODE_SAMPLE' 48 | class User extends Model 49 | { 50 | #[\Illuminate\Database\Eloquent\Attributes\Scope] 51 | public function active($query) 52 | { 53 | return $query->where('active', 1); 54 | } 55 | } 56 | CODE_SAMPLE 57 | )] 58 | ); 59 | } 60 | 61 | /** 62 | * @return array> 63 | */ 64 | public function getNodeTypes(): array 65 | { 66 | return [Class_::class]; 67 | } 68 | 69 | /** 70 | * @param Class_ $node 71 | */ 72 | public function refactor(Node $node): ?Node 73 | { 74 | if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Model'))) { 75 | return null; 76 | } 77 | 78 | if (! is_string($className = $this->getName($node))) { 79 | return null; 80 | } 81 | 82 | $classReflection = $this->reflectionProvider->getClass($className); 83 | 84 | $changes = false; 85 | foreach ($node->getMethods() as $classMethod) { 86 | $name = $this->getName($classMethod); 87 | // make sure it starts with scope and the next character is upper case 88 | if (! str_starts_with($name, 'scope') || ! ctype_upper(substr($name, 5, 1))) { 89 | continue; 90 | } 91 | 92 | $newName = lcfirst(str_replace('scope', '', $name)); 93 | 94 | if ($classReflection->hasMethod($newName)) { 95 | continue; 96 | } 97 | 98 | if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, self::SCOPE_ATTRIBUTE)) { 99 | continue; 100 | } 101 | 102 | $classMethod->flags = Modifiers::PROTECTED; 103 | $classMethod->name = new Identifier($newName); 104 | $classMethod->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified(self::SCOPE_ATTRIBUTE))]); 105 | $changes = true; 106 | } 107 | 108 | if ($changes === false) { 109 | return null; 110 | } 111 | 112 | return $node; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Rector/Class_/AnonymousMigrationsRector.php: -------------------------------------------------------------------------------- 1 | > 57 | */ 58 | public function getNodeTypes(): array 59 | { 60 | return [Class_::class]; 61 | } 62 | 63 | /** 64 | * @param Class_ $node 65 | */ 66 | public function refactor(Node $node): ?Node 67 | { 68 | if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Migrations\Migration'))) { 69 | return null; 70 | } 71 | 72 | if ($node->isAbstract()) { 73 | return null; 74 | } 75 | 76 | if ($this->classAnalyzer->isAnonymousClass($node)) { 77 | return null; 78 | } 79 | 80 | $node->name = null; 81 | 82 | return new Return_(new New_($node)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Rector/Class_/CashierStripeOptionsToStripeRector.php: -------------------------------------------------------------------------------- 1 | > 64 | */ 65 | public function getNodeTypes(): array 66 | { 67 | return [Class_::class]; 68 | } 69 | 70 | public function refactor(Node $node): ?Node 71 | { 72 | if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Model'))) { 73 | return null; 74 | } 75 | 76 | $scope = ScopeFetcher::fetch($node); 77 | 78 | if (! $this->usesBillableTrait($scope)) { 79 | return null; 80 | } 81 | 82 | /** @var Class_ $node */ 83 | $classMethod = $node->getMethod('stripeOptions'); 84 | 85 | if (! $classMethod instanceof ClassMethod) { 86 | return null; 87 | } 88 | 89 | $classMethod->name = new Identifier('stripe'); 90 | 91 | return $node; 92 | } 93 | 94 | private function usesBillableTrait(Scope $scope): bool 95 | { 96 | $classReflection = $scope->getClassReflection(); 97 | 98 | while ($classReflection instanceof ClassReflection) { 99 | foreach ($classReflection->getTraits() as $traitReflection) { 100 | if ($traitReflection->hasTraitUse('Laravel\Cashier\Billable')) { 101 | return true; 102 | } 103 | } 104 | 105 | $classReflection = $classReflection->getParentClass(); 106 | } 107 | 108 | return true; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Rector/Class_/LivewireComponentComputedMethodToComputedAttributeRector.php: -------------------------------------------------------------------------------- 1 | isObjectType($node, new ObjectType(self::COMPONENT_CLASS))) { 73 | return null; 74 | } 75 | 76 | $changes = false; 77 | 78 | foreach ($node->stmts as $stmt) { 79 | if ( 80 | $stmt instanceof ClassMethod && 81 | $stmt->isPublic() && 82 | (bool) preg_match(self::METHOD_PATTERN, $this->getName($stmt), $matches)) { 83 | $methodName = lcfirst($matches['methodName']); 84 | 85 | if ($this->methodExistsInClass($node, $methodName)) { 86 | continue; 87 | } 88 | 89 | $this->addComputedAttributeToClassMethodAndRename($stmt, $methodName); 90 | $changes = true; 91 | } 92 | } 93 | 94 | if ($changes === false) { 95 | return null; 96 | } 97 | 98 | return $node; 99 | } 100 | 101 | private function addComputedAttributeToClassMethodAndRename(ClassMethod $classMethod, string $name): void 102 | { 103 | if ($this->phpAttributeAnalyzer->hasPhpAttribute($classMethod, self::COMPUTED_ATTRIBUTE)) { 104 | return; 105 | } 106 | 107 | $classMethod->attrGroups[] = new AttributeGroup([ 108 | new Attribute( 109 | new FullyQualified(self::COMPUTED_ATTRIBUTE) 110 | ), 111 | ]); 112 | 113 | $classMethod->name = new Identifier($name); 114 | } 115 | 116 | private function methodExistsInClass(Class_ $class, string $methodName): bool 117 | { 118 | return $this->getType($class)->hasMethod($methodName)->yes(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Rector/Class_/PropertyDeferToDeferrableProviderToRector.php: -------------------------------------------------------------------------------- 1 | > 62 | */ 63 | public function getNodeTypes(): array 64 | { 65 | return [Class_::class]; 66 | } 67 | 68 | /** 69 | * @param Class_ $node 70 | */ 71 | public function refactor(Node $node): ?Node 72 | { 73 | if (! $this->isObjectType($node, new ObjectType('Illuminate\Support\ServiceProvider'))) { 74 | return null; 75 | } 76 | 77 | $deferProperty = $this->matchDeferWithFalseProperty($node); 78 | if (! $deferProperty instanceof Property) { 79 | return null; 80 | } 81 | 82 | unset($node->stmts[array_search($deferProperty, $node->stmts, true)]); 83 | 84 | $node->implements[] = new FullyQualified('Illuminate\Contracts\Support\DeferrableProvider'); 85 | 86 | return $node; 87 | } 88 | 89 | private function matchDeferWithFalseProperty(Class_ $class): ?Property 90 | { 91 | foreach ($class->getProperties() as $property) { 92 | if (! $this->isName($property, 'defer')) { 93 | continue; 94 | } 95 | 96 | $onlyProperty = $property->props[0]; 97 | if (! $onlyProperty->default instanceof Expr) { 98 | return null; 99 | } 100 | 101 | if ($this->valueResolver->isTrue($onlyProperty->default)) { 102 | return $property; 103 | } 104 | } 105 | 106 | return null; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Rector/Class_/RemoveModelPropertyFromFactoriesRector.php: -------------------------------------------------------------------------------- 1 | > 49 | */ 50 | public function getNodeTypes(): array 51 | { 52 | return [Class_::class]; 53 | } 54 | 55 | /** 56 | * @param Class_ $node 57 | */ 58 | public function refactor(Node $node): ?Node 59 | { 60 | if (! $this->isObjectType($node, new ObjectType('Illuminate\Database\Eloquent\Factories\Factory'))) { 61 | return null; 62 | } 63 | 64 | foreach ($node->stmts as $index => $stmt) { 65 | if (! $stmt instanceof Property) { 66 | continue; 67 | } 68 | 69 | if (! $this->isName($stmt, 'model')) { 70 | continue; 71 | } 72 | 73 | unset($node->stmts[$index]); 74 | 75 | break; 76 | } 77 | 78 | return $node; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Rector/Empty_/EmptyToBlankAndFilledFuncRector.php: -------------------------------------------------------------------------------- 1 | expr instanceof Empty_) { 49 | return null; 50 | } 51 | $method = 'filled'; 52 | $args = [$node->expr->expr]; 53 | } elseif ($node instanceof Empty_) { 54 | $method = 'blank'; 55 | $args = [$node->expr]; 56 | } else { 57 | return null; 58 | } 59 | 60 | return $this->nodeFactory->createFuncCall($method, $args); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Rector/Expr/AppEnvironmentComparisonToParameterRector.php: -------------------------------------------------------------------------------- 1 | environment() === \'local\'` with `$app->environment(\'local\')`', 29 | [ 30 | new CodeSample( 31 | <<<'CODE_SAMPLE' 32 | $app->environment() === 'production'; 33 | CODE_SAMPLE 34 | , 35 | <<<'CODE_SAMPLE' 36 | $app->environment('production'); 37 | CODE_SAMPLE 38 | ), 39 | ] 40 | ); 41 | } 42 | 43 | public function getNodeTypes(): array 44 | { 45 | return [Expr::class]; 46 | } 47 | 48 | public function refactor(Node $node): MethodCall|StaticCall|null 49 | { 50 | if (! $node instanceof Identical && ! $node instanceof Equal) { 51 | return null; 52 | } 53 | 54 | /** @var MethodCall|StaticCall|null $methodCall */ 55 | $methodCall = array_values( 56 | array_filter( 57 | [$node->left, $node->right], 58 | fn ($node) => ($node instanceof MethodCall || $node instanceof StaticCall) && $this->isName( 59 | $node->name, 60 | 'environment' 61 | ) 62 | ) 63 | )[0] ?? null; 64 | 65 | if ($methodCall === null || ! $this->validMethodCall($methodCall)) { 66 | return null; 67 | } 68 | 69 | /** @var Expr $otherNode */ 70 | $otherNode = array_values( 71 | array_filter([$node->left, $node->right], static fn ($node) => $node !== $methodCall) 72 | )[0] ?? null; 73 | 74 | if (! $otherNode instanceof String_) { 75 | return null; 76 | } 77 | 78 | // make sure the method call has no arguments 79 | if ($methodCall->getArgs() !== []) { 80 | return null; 81 | } 82 | 83 | $methodCall->args[] = new Arg($otherNode); 84 | 85 | return $methodCall; 86 | } 87 | 88 | private function validMethodCall(MethodCall|StaticCall $methodCall): bool 89 | { 90 | return match (true) { 91 | $methodCall instanceof MethodCall && $this->isObjectType( 92 | $methodCall->var, 93 | new ObjectType('Illuminate\Contracts\Foundation\Application') 94 | ) => true, 95 | $methodCall instanceof StaticCall && $this->isObjectType( 96 | $methodCall->class, 97 | new ObjectType('Illuminate\Support\Facades\App') 98 | ) => true, 99 | $methodCall instanceof StaticCall && $this->isObjectType( 100 | $methodCall->class, 101 | new ObjectType('App') 102 | ) => true, 103 | default => false, 104 | }; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Rector/Expr/SubStrToStartsWithOrEndsWithStaticMethodCallRector/SubStrToStartsWithOrEndsWithStaticMethodCallRector.php: -------------------------------------------------------------------------------- 1 | left, $node->right], fn ($node) => $node instanceof FuncCall && $this->isName( 64 | $node, 65 | 'substr' 66 | )) 67 | )[0] ?? null; 68 | 69 | if (! $functionCall instanceof FuncCall) { 70 | return null; 71 | } 72 | 73 | /** @var Expr $otherNode */ 74 | $otherNode = array_values( 75 | array_filter([$node->left, $node->right], static fn ($node) => $node !== $functionCall) 76 | )[0] ?? null; 77 | 78 | // get the function call second argument value 79 | if (count($functionCall->getArgs()) < 2) { 80 | return null; 81 | } 82 | 83 | $secondArgument = $this->valueResolver->getValue($functionCall->getArgs()[1]->value); 84 | 85 | if (! is_int($secondArgument)) { 86 | return null; 87 | } 88 | 89 | if ($secondArgument < 0 && isset($functionCall->getArgs()[2])) { 90 | return null; 91 | } 92 | 93 | $methodName = $this->getStaticMethodName($secondArgument); 94 | 95 | if ($methodName === null) { 96 | return null; 97 | } 98 | 99 | return $this->nodeFactory->createStaticCall('Illuminate\Support\Str', $methodName, [ 100 | $functionCall->getArgs()[0]->value, 101 | $otherNode, 102 | ]); 103 | } 104 | 105 | protected function getStaticMethodName(int $secondArgument): ?string 106 | { 107 | if ($secondArgument === 0) { 108 | return 'startsWith'; 109 | } 110 | 111 | if ($secondArgument < 0) { 112 | return 'endsWith'; 113 | } 114 | 115 | return null; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/FactoryFuncCallToStaticCallRector.php: -------------------------------------------------------------------------------- 1 | > 44 | */ 45 | public function getNodeTypes(): array 46 | { 47 | return [FuncCall::class]; 48 | } 49 | 50 | /** 51 | * @param FuncCall $node 52 | */ 53 | public function refactor(Node $node): ?Node 54 | { 55 | if (! $this->isName($node->name, self::FACTORY)) { 56 | return null; 57 | } 58 | 59 | if (! isset($node->args[0])) { 60 | return null; 61 | } 62 | 63 | if (! $node->args[0] instanceof Arg) { 64 | return null; 65 | } 66 | 67 | $firstArgValue = $node->args[0]->value; 68 | if (! $firstArgValue instanceof ClassConstFetch) { 69 | return null; 70 | } 71 | 72 | $model = $firstArgValue->class; 73 | 74 | // create model 75 | if (! isset($node->args[1])) { 76 | return new StaticCall($model, self::FACTORY); 77 | } 78 | 79 | // create models of a given type 80 | return new StaticCall($model, self::FACTORY, [$node->args[1]]); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/HelperFuncCallToFacadeClassRector.php: -------------------------------------------------------------------------------- 1 | trans('value'); 31 | } 32 | } 33 | CODE_SAMPLE 34 | 35 | , 36 | <<<'CODE_SAMPLE' 37 | class SomeClass 38 | { 39 | public function run() 40 | { 41 | return \Illuminate\Support\Facades\App::make('translator')->trans('value'); 42 | } 43 | } 44 | CODE_SAMPLE 45 | ), 46 | ]); 47 | } 48 | 49 | /** 50 | * @return array> 51 | */ 52 | public function getNodeTypes(): array 53 | { 54 | return [FuncCall::class]; 55 | } 56 | 57 | /** 58 | * @param FuncCall $node 59 | */ 60 | public function refactor(Node $node): ?Node 61 | { 62 | if (! $this->isNames($node->name, ['app', 'resolve'])) { 63 | return null; 64 | } 65 | 66 | if (count($node->args) > 0) { 67 | return $this->nodeFactory->createStaticCall('Illuminate\Support\Facades\App', 'make', $node->args); 68 | } 69 | 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/NotFilledBlankFuncCallToBlankFilledFuncCallRector.php: -------------------------------------------------------------------------------- 1 | expr instanceof FuncCall) { 51 | return null; 52 | } 53 | 54 | if ( 55 | ! $this->isName($node->expr->name, 'filled') && 56 | ! $this->isName($node->expr->name, 'blank') 57 | ) { 58 | return null; 59 | } 60 | 61 | $method = $this->isName($node->expr->name, 'filled') ? 'blank' : 'filled'; 62 | 63 | return $this->nodeFactory->createFuncCall($method, $node->expr->args); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/NowFuncWithStartOfDayMethodCallToTodayFuncRector.php: -------------------------------------------------------------------------------- 1 | startOfDay()', [ 22 | new CodeSample( 23 | <<<'CODE_SAMPLE' 24 | $now = now()->startOfDay(); 25 | CODE_SAMPLE 26 | , 27 | <<<'CODE_SAMPLE' 28 | $now = today(); 29 | CODE_SAMPLE 30 | ), 31 | ]); 32 | } 33 | 34 | public function getNodeTypes(): array 35 | { 36 | return [MethodCall::class]; 37 | } 38 | 39 | /** 40 | * @param MethodCall $node 41 | */ 42 | public function refactor(Node $node): ?FuncCall 43 | { 44 | if (! $this->isName($node->name, 'startOfDay')) { 45 | return null; 46 | } 47 | 48 | if (! $this->isName($node->var, 'now')) { 49 | return null; 50 | } 51 | 52 | return $this->nodeFactory->createFuncCall('today'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/RemoveDumpDataDeadCodeRector.php: -------------------------------------------------------------------------------- 1 | > 73 | */ 74 | public function getNodeTypes(): array 75 | { 76 | return [Expression::class]; 77 | } 78 | 79 | /** 80 | * @param Expression $node 81 | * @return NodeVisitor::REMOVE_NODE|null 82 | */ 83 | public function refactor(Node $node): ?int 84 | { 85 | if (! $node->expr instanceof FuncCall) { 86 | return null; 87 | } 88 | 89 | if (! $this->isNames($node->expr->name, $this->dumpFunctionNames)) { 90 | return null; 91 | } 92 | 93 | return NodeVisitor::REMOVE_NODE; 94 | } 95 | 96 | /** 97 | * @param mixed[] $configuration 98 | */ 99 | public function configure(array $configuration): void 100 | { 101 | Assert::allString($configuration); 102 | 103 | $this->dumpFunctionNames = $configuration; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/RemoveRedundantValueCallsRector.php: -------------------------------------------------------------------------------- 1 | something();', 24 | '(new Object())->something();' 25 | ), 26 | ]); 27 | } 28 | 29 | public function getNodeTypes(): array 30 | { 31 | return [FuncCall::class]; 32 | } 33 | 34 | public function refactor(Node $node): ?Node 35 | { 36 | if (! $node instanceof FuncCall) { 37 | return null; 38 | } 39 | 40 | if (! $node->name instanceof Name) { 41 | return null; 42 | } 43 | 44 | if (! $this->isName($node->name, 'value')) { 45 | return null; 46 | } 47 | 48 | $args = $node->getArgs(); 49 | 50 | if (count($args) !== 1) { 51 | return null; 52 | } 53 | 54 | if ($this->getType($args[0]->value)->isSuperTypeOf(new ClosureType([], new MixedType, true))->no() === false) { 55 | return null; 56 | } 57 | 58 | return $args[0]->value; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/RemoveRedundantWithCallsRector.php: -------------------------------------------------------------------------------- 1 | something();', 22 | '(new Object())->something();' 23 | ), 24 | ]); 25 | } 26 | 27 | public function getNodeTypes(): array 28 | { 29 | return [FuncCall::class]; 30 | } 31 | 32 | public function refactor(Node $node): ?Node 33 | { 34 | if (! $node instanceof FuncCall) { 35 | return null; 36 | } 37 | 38 | if (! $node->name instanceof Name) { 39 | return null; 40 | } 41 | 42 | if (! $this->isName($node->name, 'with')) { 43 | return null; 44 | } 45 | 46 | $args = $node->getArgs(); 47 | 48 | if (count($args) < 1 || count($args) > 2) { 49 | return null; 50 | } 51 | 52 | if (count($args) === 2) { 53 | $secondArgumentType = $this->getType($args[1]->value); 54 | 55 | if ($secondArgumentType->isCallable()->no() === false) { 56 | return null; 57 | } 58 | } 59 | 60 | return $args[0]->value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/SleepFuncToSleepStaticCallRector.php: -------------------------------------------------------------------------------- 1 | expr instanceof FuncCall) { 49 | return null; 50 | } 51 | 52 | if (! $this->isName($node->expr->name, 'sleep') && ! $this->isName($node->expr->name, 'usleep')) { 53 | return null; 54 | } 55 | 56 | $method = $this->isName($node->expr->name, 'sleep') ? 'sleep' : 'usleep'; 57 | 58 | $node->expr = $this->nodeFactory->createStaticCall('Illuminate\Support\Sleep', $method, $node->expr->args); 59 | 60 | return $node; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Rector/FuncCall/ThrowIfAndThrowUnlessExceptionsToUseClassStringRector.php: -------------------------------------------------------------------------------- 1 | isNames($node, ['throw_if', 'throw_unless'])) { 45 | return null; 46 | } 47 | 48 | if (count($node->args) !== 2 || ! $node->args[1] instanceof Arg) { 49 | return null; 50 | } 51 | 52 | $exception = $node->args[1]->value; 53 | if (! $exception instanceof New_) { 54 | return null; 55 | } 56 | 57 | $class = $exception->class; 58 | if (! $class instanceof Name) { 59 | return null; 60 | } 61 | 62 | // convert the class to a class string 63 | $node->args[1] = new Arg(new ClassConstFetch($class, 'class')); 64 | $node->args = [ 65 | ...$node->args, 66 | ...$exception->getArgs(), 67 | ]; 68 | 69 | return $node; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/AssertSeeToAssertSeeHtmlRector.php: -------------------------------------------------------------------------------- 1 | 'assertSeeHtml', 25 | 'assertDontSee' => 'assertDontSeeHtml', 26 | 'assertSeeInOrder' => 'assertSeeHtmlInOrder', 27 | ]; 28 | 29 | public function __construct( 30 | private readonly ValueResolver $valueResolver 31 | ) {} 32 | 33 | public function getRuleDefinition(): RuleDefinition 34 | { 35 | return new RuleDefinition( 36 | 'Replace assertSee with assertSeeHtml when testing HTML with escape set to false', 37 | [ 38 | new CodeSample( 39 | '$response->assertSee("
  • foo
  • ", false);', 40 | '$response->assertSeeHtml("
  • foo
  • ");' 41 | ), 42 | new CodeSample( 43 | '$response->assertDontSee("
  • foo
  • ", false);', 44 | '$response->assertDontSeeHtml("
  • foo
  • ");' 45 | ), 46 | new CodeSample( 47 | '$response->assertSeeInOrder(["
  • foo
  • ", "
  • bar
  • "], false);', 48 | '$response->assertSeeHtmlInOrder(["
  • foo
  • ", "
  • bar
  • "]);' 49 | ), 50 | ] 51 | ); 52 | } 53 | 54 | /** 55 | * @return array> 56 | */ 57 | public function getNodeTypes(): array 58 | { 59 | return [MethodCall::class]; 60 | } 61 | 62 | /** 63 | * @param MethodCall $node 64 | */ 65 | public function refactor(Node $node): ?Node 66 | { 67 | if (! $this->isObjectType($node->var, new ObjectType('Illuminate\Testing\TestResponse'))) { 68 | return null; 69 | } 70 | 71 | $methodCallName = (string) $this->getName($node->name); 72 | 73 | if (! array_key_exists($methodCallName, $this->methodsToReplace)) { 74 | return null; 75 | } 76 | 77 | if (count($node->getArgs()) !== 2) { 78 | return null; 79 | } 80 | 81 | if (! $this->valueResolver->isFalse($node->getArgs()[1]->value)) { 82 | return null; 83 | } 84 | 85 | return $this->nodeFactory->createMethodCall( 86 | $node->var, 87 | $this->methodsToReplace[$methodCallName], 88 | [$node->getArgs()[0]] 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/AvoidNegatedCollectionFilterOrRejectRector.php: -------------------------------------------------------------------------------- 1 | filter(fn (?int $number): bool => ! is_null($number)); 34 | $collection->filter(fn (?int $number): bool => ! $number); 35 | $collection->reject(fn (?int $number) => ! $number > 0); 36 | CODE_SAMPLE 37 | , 38 | <<<'CODE_SAMPLE' 39 | use Illuminate\Support\Collection; 40 | 41 | $collection = new Collection([0, 1, null, -1]); 42 | $collection->reject(fn (?int $number): bool => is_null($number)); // Avoid negation 43 | $collection->reject(fn (?int $number): bool => (bool) $number); // Explicitly cast 44 | $collection->filter(fn (?int $number): bool => $number > 0); // Adds return type 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): ?Node 63 | { 64 | return $this->updateFilterOrRejectCall($node); 65 | } 66 | 67 | private function updateFilterOrRejectCall(MethodCall $methodCall): ?MethodCall 68 | { 69 | if (! $this->isObjectType($methodCall->var, new ObjectType('Illuminate\Support\Enumerable'))) { 70 | return null; 71 | } 72 | 73 | if (! $this->isNames($methodCall->name, ['filter', 'reject'])) { 74 | return null; 75 | } 76 | 77 | $args = $methodCall->getArgs(); 78 | if (count($args) !== 1) { 79 | return null; 80 | } 81 | 82 | $arg = $args[0]; 83 | $argValue = $arg->value; 84 | 85 | if (! $argValue instanceof ArrowFunction) { 86 | return null; 87 | } 88 | 89 | $return = $argValue->expr; 90 | if (! $return instanceof BooleanNot) { 91 | return null; 92 | } 93 | 94 | $methodCall->name = new Identifier( 95 | $this->isName($methodCall->name, 'filter') 96 | ? 'reject' 97 | : 'filter' 98 | ); 99 | 100 | // Since negation implicitly casts to boolean, we need to replace it with an 101 | // explicit cast - unless the value is already a boolean. 102 | $returnExpr = $return->expr; 103 | $argValue->expr = $this->getType($returnExpr)->isBoolean()->yes() 104 | ? $returnExpr 105 | : new Bool_($returnExpr); 106 | 107 | $argValue->returnType = new Identifier('bool'); 108 | 109 | return $methodCall; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/ContainerBindConcreteWithClosureOnlyRector.php: -------------------------------------------------------------------------------- 1 | app->bind(SomeClass::class, function (): SomeClass { 33 | return new SomeClass(); 34 | }); 35 | CODE_SAMPLE 36 | , 37 | <<<'CODE_SAMPLE' 38 | $this->app->bind(function (): SomeClass { 39 | return new SomeClass(); 40 | }); 41 | CODE_SAMPLE 42 | ), 43 | ] 44 | ); 45 | } 46 | 47 | public function getNodeTypes(): array 48 | { 49 | return [MethodCall::class]; 50 | } 51 | 52 | /** 53 | * @param MethodCall $node 54 | */ 55 | public function refactor(Node $node): ?MethodCall 56 | { 57 | if (! $this->isNames($node->name, ['bind', 'singleton', 'bindIf', 'singletonIf'])) { 58 | return null; 59 | } 60 | 61 | if (! $this->isObjectType($node->var, new ObjectType('Illuminate\Contracts\Container\Container'))) { 62 | return null; 63 | } 64 | 65 | if ($node->isFirstClassCallable()) { 66 | return null; 67 | } 68 | 69 | if (count($node->getArgs()) < 2) { 70 | return null; 71 | } 72 | 73 | $type = $this->getType($node->getArgs()[0]->value); 74 | $classString = $node->getArgs()[0]->value; 75 | $concreteNode = $node->getArgs()[1]->value; 76 | 77 | if (! $concreteNode instanceof Closure) { 78 | return null; 79 | } 80 | $abstractFromConcrete = $this->returnTypeInferer->inferFunctionLike($concreteNode); 81 | 82 | if ($classString instanceof Const_ 83 | && $this->isName($classString, 'class')) { 84 | return null; 85 | } 86 | 87 | $abstractObjectType = $type->getClassStringObjectType(); 88 | 89 | if ($abstractFromConcrete->isSuperTypeOf($abstractObjectType)->no()) { 90 | return null; 91 | } 92 | 93 | // set the concrete's return type of the closure to from what's determined in PHPStan 94 | $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($abstractObjectType, TypeKind::RETURN); 95 | if (! $returnTypeNode instanceof Node) { 96 | return null; 97 | } 98 | 99 | $concreteNode->returnType = $returnTypeNode; 100 | 101 | $args = $node->getArgs(); 102 | 103 | $node->args = array_splice($args, 1); 104 | 105 | return $node; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/DatabaseExpressionToStringToMethodCallRector.php: -------------------------------------------------------------------------------- 1 | __toString(); 27 | CODE_SAMPLE 28 | , 29 | <<<'CODE_SAMPLE' 30 | use Illuminate\Support\Facades\DB; 31 | 32 | $string = DB::raw('select 1')->getValue(DB::connection()->getQueryGrammar()); 33 | CODE_SAMPLE 34 | ), 35 | ]); 36 | } 37 | 38 | /** 39 | * @return array> 40 | */ 41 | public function getNodeTypes(): array 42 | { 43 | return [MethodCall::class]; 44 | } 45 | 46 | /** 47 | * @param MethodCall $node 48 | */ 49 | public function refactor(Node $node): ?Node 50 | { 51 | if (! $this->isName($node->name, '__toString')) { 52 | return null; 53 | } 54 | 55 | if (! $node->var instanceof StaticCall) { 56 | return null; 57 | } 58 | 59 | if (! $this->isName($node->var->class, 'Illuminate\Support\Facades\DB')) { 60 | return null; 61 | } 62 | 63 | if (! $this->isName($node->var->name, 'raw')) { 64 | return null; 65 | } 66 | 67 | return $this->nodeFactory->createMethodCall($node->var, 'getValue', [ 68 | $this->nodeFactory->createMethodCall( 69 | $this->nodeFactory->createStaticCall('Illuminate\Support\Facades\DB', 'connection'), 70 | 'getQueryGrammar' 71 | ), 72 | ]); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/FactoryApplyingStatesRector.php: -------------------------------------------------------------------------------- 1 | state('delinquent'); 37 | $factory->states('premium', 'delinquent'); 38 | CODE_SAMPLE 39 | 40 | , 41 | <<<'CODE_SAMPLE' 42 | $factory->delinquent(); 43 | $factory->premium()->delinquent(); 44 | CODE_SAMPLE 45 | ), 46 | ]); 47 | } 48 | 49 | /** 50 | * @return array> 51 | */ 52 | public function getNodeTypes(): array 53 | { 54 | return [MethodCall::class]; 55 | } 56 | 57 | /** 58 | * @param MethodCall $node 59 | */ 60 | public function refactor(Node $node): ?Node 61 | { 62 | if (! $this->isNames($node->name, ['state', 'states'])) { 63 | return null; 64 | } 65 | 66 | if (! $this->isObjectType($node->var, new ObjectType('Illuminate\Database\Eloquent\FactoryBuilder'))) { 67 | return null; 68 | } 69 | 70 | $var = $node->var; 71 | $states = $this->getStatesFromArgs($node->args); 72 | 73 | Assert::allString($states); 74 | 75 | foreach ($states as $state) { 76 | $var = $this->nodeFactory->createMethodCall($var, $state); 77 | } 78 | 79 | return $var; 80 | } 81 | 82 | /** 83 | * @param array $args 84 | * @return mixed[] 85 | */ 86 | private function getStatesFromArgs(array $args): array 87 | { 88 | if (count($args) === 1 && isset($args[0]) && $args[0] instanceof Arg) { 89 | return (array) $this->valueResolver->getValue($args[0]->value); 90 | } 91 | 92 | return array_map(fn ($arg) => $arg instanceof Arg ? $this->valueResolver->getValue($arg->value) : null, $args); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/LumenRoutesStringActionToUsesArrayRector.php: -------------------------------------------------------------------------------- 1 | get('/user', 'UserController@get'); 33 | CODE_SAMPLE 34 | , <<<'CODE_SAMPLE' 35 | $router->get('/user', ['uses => 'UserController@get']); 36 | CODE_SAMPLE)] 37 | ); 38 | } 39 | 40 | /** 41 | * @return array> 42 | */ 43 | public function getNodeTypes(): array 44 | { 45 | return [MethodCall::class]; 46 | } 47 | 48 | public function refactor(Node $node): ?Node 49 | { 50 | if (! $node instanceof MethodCall) { 51 | return null; 52 | } 53 | 54 | if (! $this->lumenRouteRegisteringMethodAnalyzer->isLumenRoutingClass($node)) { 55 | return null; 56 | } 57 | 58 | if (! $this->lumenRouteRegisteringMethodAnalyzer->isRoutesRegisterRoute($node->name)) { 59 | return null; 60 | } 61 | 62 | $string = $node->getArgs()[1] 63 | ->value; 64 | if (! $string instanceof String_) { 65 | return null; 66 | } 67 | 68 | $node->args[1] = new Arg(new Array_([new ArrayItem($string, new String_('uses'))])); 69 | 70 | return $node; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/RedirectBackToBackHelperRector.php: -------------------------------------------------------------------------------- 1 | back()` and `Redirect::back()` with `back()`', 25 | [ 26 | new CodeSample( 27 | <<<'CODE_SAMPLE' 28 | use Illuminate\Support\Facades\Redirect; 29 | 30 | class MyController 31 | { 32 | public function store() 33 | { 34 | return redirect()->back()->with('error', 'Incorrect Details.') 35 | } 36 | 37 | public function update() 38 | { 39 | return Redirect::back()->with('error', 'Incorrect Details.') 40 | } 41 | } 42 | CODE_SAMPLE 43 | , 44 | <<<'CODE_SAMPLE' 45 | use Illuminate\Support\Facades\Redirect; 46 | 47 | class MyController 48 | { 49 | public function store() 50 | { 51 | return back()->with('error', 'Incorrect Details.') 52 | } 53 | 54 | public function update() 55 | { 56 | return back()->with('error', 'Incorrect Details.') 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 ($node instanceof MethodCall) { 79 | return $this->updateRedirectHelperCall($node); 80 | } 81 | 82 | return $this->updateRedirectStaticCall($node); 83 | } 84 | 85 | private function updateRedirectHelperCall(MethodCall $methodCall): ?FuncCall 86 | { 87 | if (! $this->isName($methodCall->name, 'back')) { 88 | return null; 89 | } 90 | 91 | if (! $methodCall->var instanceof FuncCall) { 92 | return null; 93 | } 94 | 95 | if ($methodCall->var->getArgs() !== []) { 96 | return null; 97 | } 98 | 99 | if (! $this->isName($methodCall->var->name, 'redirect')) { 100 | return null; 101 | } 102 | 103 | $methodCall->var->name = new Name('back'); 104 | $methodCall->var->args = $methodCall->getArgs(); 105 | 106 | return $methodCall->var; 107 | } 108 | 109 | private function updateRedirectStaticCall(StaticCall $staticCall): ?FuncCall 110 | { 111 | if (! $this->isName($staticCall->class, 'Illuminate\Support\Facades\Redirect')) { 112 | return null; 113 | } 114 | 115 | if (! $this->isName($staticCall->name, 'back')) { 116 | return null; 117 | } 118 | 119 | return new FuncCall(new Name('back'), $staticCall->args); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/RedirectRouteToToRouteHelperRector.php: -------------------------------------------------------------------------------- 1 | route("home")` and `Redirect::route("home")` with `to_route("home")`', 25 | [ 26 | new CodeSample( 27 | <<<'CODE_SAMPLE' 28 | use Illuminate\Support\Facades\Redirect; 29 | 30 | class MyController 31 | { 32 | public function store() 33 | { 34 | return redirect()->route('home')->with('error', 'Incorrect Details.') 35 | } 36 | 37 | public function update() 38 | { 39 | return Redirect::route('home')->with('error', 'Incorrect Details.') 40 | } 41 | } 42 | CODE_SAMPLE 43 | , 44 | <<<'CODE_SAMPLE' 45 | use Illuminate\Support\Facades\Redirect; 46 | 47 | class MyController 48 | { 49 | public function store() 50 | { 51 | return to_route('home')->with('error', 'Incorrect Details.') 52 | } 53 | 54 | public function update() 55 | { 56 | return to_route('home')->with('error', 'Incorrect Details.') 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 ($node instanceof MethodCall) { 79 | return $this->updateRedirectHelperCall($node); 80 | } 81 | 82 | return $this->updateRedirectStaticCall($node); 83 | } 84 | 85 | private function updateRedirectHelperCall(MethodCall $methodCall): ?FuncCall 86 | { 87 | if (! $this->isName($methodCall->name, 'route')) { 88 | return null; 89 | } 90 | 91 | if (! $methodCall->var instanceof FuncCall) { 92 | return null; 93 | } 94 | 95 | if ($methodCall->var->getArgs() !== []) { 96 | return null; 97 | } 98 | 99 | if (! $this->isName($methodCall->var->name, 'redirect')) { 100 | return null; 101 | } 102 | 103 | $methodCall->var->name = new Name('to_route'); 104 | $methodCall->var->args = $methodCall->getArgs(); 105 | 106 | return $methodCall->var; 107 | } 108 | 109 | private function updateRedirectStaticCall(StaticCall $staticCall): ?FuncCall 110 | { 111 | if (! $this->isName($staticCall->class, 'Illuminate\Support\Facades\Redirect')) { 112 | return null; 113 | } 114 | 115 | if (! $this->isName($staticCall->name, 'route')) { 116 | return null; 117 | } 118 | 119 | return new FuncCall(new Name('to_route'), $staticCall->args); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/RefactorBlueprintGeometryColumnsRector.php: -------------------------------------------------------------------------------- 1 | point('coordinates')->spatialIndex(); 26 | CODE_SAMPLE, 27 | <<<'CODE_SAMPLE' 28 | $blueprint->geometry('coordinates', 'point')->spatialIndex(); 29 | CODE_SAMPLE, 30 | )] 31 | ); 32 | } 33 | 34 | public function getNodeTypes(): array 35 | { 36 | return [MethodCall::class]; 37 | } 38 | 39 | /** 40 | * @param MethodCall $node 41 | */ 42 | public function refactor(Node $node): ?MethodCall 43 | { 44 | if (! $this->isNames($node->name, [ 45 | 'point', 46 | 'linestring', 47 | 'polygon', 48 | 'geometrycollection', 49 | 'multipoint', 50 | 'multilinestring', 51 | 'multipolygon', 52 | 53 | ])) { 54 | return null; 55 | } 56 | 57 | if (! $node->name instanceof Identifier) { 58 | return null; 59 | } 60 | 61 | if (! $this->isObjectType($node->var, new ObjectType('Illuminate\Database\Schema\Blueprint'))) { 62 | return null; 63 | } 64 | 65 | $previousName = $node->name; 66 | 67 | $node->name = new Identifier('geometry'); 68 | $node->args[] = new Arg(new String_($previousName->name)); 69 | 70 | return $node; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/ReplaceWithoutJobsEventsAndNotificationsWithFacadeFakeRector.php: -------------------------------------------------------------------------------- 1 | withoutJobs(); 27 | $this->withoutEvents(); 28 | $this->withoutNotifications(); 29 | CODE_SAMPLE, 30 | <<<'CODE_SAMPLE' 31 | \Illuminate\Support\Facades\Bus::fake(); 32 | \Illuminate\Support\Facades\Event::fake(); 33 | \Illuminate\Support\Facades\Notification::fake(); 34 | CODE_SAMPLE, 35 | ), 36 | ] 37 | ); 38 | } 39 | 40 | public function getNodeTypes(): array 41 | { 42 | return [MethodCall::class]; 43 | } 44 | 45 | /** 46 | * @param MethodCall $node 47 | */ 48 | public function refactor(Node $node): ?StaticCall 49 | { 50 | if (! $this->isNames($node->name, ['withoutJobs', 'withoutEvents', 'withoutNotifications'])) { 51 | return null; 52 | } 53 | 54 | if (! $this->isObjectType($node->var, new ObjectType('Illuminate\Foundation\Testing\TestCase'))) { 55 | return null; 56 | } 57 | 58 | if (! $node->name instanceof Identifier) { 59 | return null; 60 | } 61 | 62 | $facade = match ($node->name->name) { 63 | 'withoutJobs' => 'Bus', 64 | 'withoutEvents' => 'Event', 65 | 'withoutNotifications' => 'Notification', 66 | default => null, 67 | }; 68 | 69 | if ($facade === null) { 70 | return null; 71 | } 72 | 73 | return $this->nodeFactory->createStaticCall('Illuminate\Support\Facades\\' . $facade, 'fake'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/ResponseHelperCallToJsonResponseRector.php: -------------------------------------------------------------------------------- 1 | json()', [ 24 | new CodeSample( 25 | <<<'CODE_SAMPLE' 26 | response()->json(['key' => 'value']); 27 | CODE_SAMPLE 28 | 29 | , 30 | <<<'CODE_SAMPLE' 31 | return new JsonResponse(['key' => 'value']); 32 | CODE_SAMPLE 33 | ), 34 | ]); 35 | } 36 | 37 | /** 38 | * @return array> 39 | */ 40 | public function getNodeTypes(): array 41 | { 42 | return [MethodCall::class]; 43 | } 44 | 45 | /** 46 | * @param MethodCall $node 47 | */ 48 | public function refactor(Node $node): ?Node 49 | { 50 | if (! $this->isName($node->name, 'json')) { 51 | return null; 52 | } 53 | 54 | if (! $this->isName($node->var, 'response')) { 55 | return null; 56 | } 57 | 58 | if (! $node->var instanceof FuncCall) { 59 | return null; 60 | } 61 | 62 | if ($node->var->args !== []) { 63 | return null; 64 | } 65 | 66 | return new New_(new FullyQualified('Illuminate\Http\JsonResponse'), $node->args); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/ReverseConditionableMethodCallRector.php: -------------------------------------------------------------------------------- 1 | when(!$condition, function () {}); 29 | CODE_SAMPLE, 30 | <<<'CODE_SAMPLE' 31 | $conditionable->unless($condition, function () {}); 32 | CODE_SAMPLE 33 | ), 34 | new CodeSample(<<<'CODE_SAMPLE' 35 | $conditionable->unless(!$condition, function () {}); 36 | CODE_SAMPLE, 37 | <<<'CODE_SAMPLE' 38 | $conditionable->when($condition, function () {}); 39 | CODE_SAMPLE 40 | ), 41 | ] 42 | ); 43 | } 44 | 45 | public function getNodeTypes(): array 46 | { 47 | return [MethodCall::class]; 48 | } 49 | 50 | /** 51 | * @param MethodCall $node 52 | */ 53 | public function refactor(Node $node): ?MethodCall 54 | { 55 | if (! $this->isObjectType($node->var, new ObjectType(self::CONDITIONABLE_TRAIT))) { 56 | return null; 57 | } 58 | 59 | if (! $this->isNames($node->name, ['when', 'unless'])) { 60 | return null; 61 | } 62 | 63 | if ($node->isFirstClassCallable()) { 64 | return null; 65 | } 66 | 67 | if ($node->getArgs() === []) { 68 | return null; 69 | } 70 | 71 | $arg = $node->getArgs()[0]; 72 | 73 | if (! $node->name instanceof Identifier) { 74 | return null; 75 | } 76 | 77 | if ($arg->value instanceof BooleanNot) { 78 | $node->args[0] = new Arg($arg->value->expr); 79 | $name = $node->name->toString() === 'when' ? 'unless' : 'when'; 80 | 81 | $node->name = new Identifier($name); 82 | 83 | return $node; 84 | } 85 | 86 | return null; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Rector/MethodCall/UnaliasCollectionMethodsRector.php: -------------------------------------------------------------------------------- 1 | average(); 31 | $collection->some(fn (?int $number): bool => is_null($number)); 32 | CODE_SAMPLE 33 | , 34 | <<<'CODE_SAMPLE' 35 | use Illuminate\Support\Collection; 36 | 37 | $collection = new Collection([0, 1, null, -1]); 38 | $collection->avg(); 39 | $collection->contains(fn (?int $number): bool => is_null($number)); 40 | CODE_SAMPLE 41 | ), 42 | ] 43 | ); 44 | } 45 | 46 | /** 47 | * @return array> 48 | */ 49 | public function getNodeTypes(): array 50 | { 51 | return [MethodCall::class]; 52 | } 53 | 54 | /** 55 | * @param MethodCall $node 56 | */ 57 | public function refactor(Node $node): ?Node 58 | { 59 | return $this->updateMethodCall($node); 60 | } 61 | 62 | private function updateMethodCall(MethodCall $methodCall): ?MethodCall 63 | { 64 | if (! $this->isObjectType($methodCall->var, new ObjectType('Illuminate\Support\Enumerable'))) { 65 | return null; 66 | } 67 | 68 | $name = $methodCall->name; 69 | if ($this->isName($name, 'some')) { 70 | $replacement = 'contains'; 71 | } elseif ($this->isName($name, 'average')) { 72 | $replacement = 'avg'; 73 | } elseif ($this->isName($name, 'unlessEmpty')) { 74 | $replacement = 'whenNotEmpty'; 75 | } elseif ($this->isName($name, 'unlessNotEmpty')) { 76 | $replacement = 'whenEmpty'; 77 | } else { 78 | return null; 79 | } 80 | 81 | $methodCall->name = new Identifier($replacement); 82 | 83 | return $methodCall; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Rector/New_/AddGuardToLoginEventRector.php: -------------------------------------------------------------------------------- 1 | > 63 | */ 64 | public function getNodeTypes(): array 65 | { 66 | return [Expression::class]; 67 | } 68 | 69 | /** 70 | * @param Expression $node 71 | * @return array|null 72 | */ 73 | public function refactor(Node $node): ?array 74 | { 75 | $newNode = $this->getNewNode($node); 76 | 77 | if (! $newNode instanceof New_) { 78 | return null; 79 | } 80 | 81 | if (! $this->isName($newNode->class, 'Illuminate\Auth\Events\Login')) { 82 | return null; 83 | } 84 | 85 | if (count($newNode->args) === 3) { 86 | return null; 87 | } 88 | 89 | $guardVariable = new Variable('guard'); 90 | $assign = $this->createGuardAssign($guardVariable); 91 | 92 | $newNode->args = array_merge([new Arg($guardVariable)], $newNode->args); 93 | 94 | return [new Expression($assign), $node]; 95 | } 96 | 97 | private function createGuardAssign(Variable $guardVariable): Assign 98 | { 99 | $string = new String_('auth.defaults.guard'); 100 | 101 | return new Assign($guardVariable, $this->nodeFactory->createFuncCall('config', [$string])); 102 | } 103 | 104 | private function getNewNode(Expression $expression): ?New_ 105 | { 106 | if ($expression->expr instanceof Assign && $expression->expr->expr instanceof New_) { 107 | return $expression->expr->expr; 108 | } 109 | 110 | if ($expression->expr instanceof New_) { 111 | return $expression->expr; 112 | } 113 | 114 | return null; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Rector/StaticCall/AssertWithClassStringToTypeHintedClosureRector.php: -------------------------------------------------------------------------------- 1 | > 48 | */ 49 | public function getNodeTypes(): array 50 | { 51 | return [StaticCall::class]; 52 | } 53 | 54 | /** 55 | * @param StaticCall $node 56 | */ 57 | public function refactor(Node $node): ?StaticCall 58 | { 59 | if (! $this->facadeAssertionAnalyzer->isFacadeAssertion($node)) { 60 | return null; 61 | } 62 | 63 | if ($node->isFirstClassCallable()) { 64 | return null; 65 | } 66 | 67 | if (count($node->getArgs()) !== 2) { 68 | return null; 69 | } 70 | 71 | if (! $node->args[1] instanceof Arg) { 72 | return null; 73 | } 74 | 75 | $functionLike = $node->args[1]->value; 76 | 77 | if ( 78 | (! $functionLike instanceof Closure 79 | && ! $functionLike instanceof ArrowFunction) 80 | || ! isset($functionLike->params[0]) 81 | ) { 82 | return null; 83 | } 84 | 85 | if (! $node->args[0] instanceof Arg) { 86 | return null; 87 | } 88 | 89 | $type = $this->getType($node->args[0]->value); 90 | 91 | $classString = match (true) { 92 | /** @phpstan-ignore method.notFound */ 93 | $type->isClassString()->yes() => $type->getClassStringObjectType()->getClassName(), 94 | $type->isString()->yes() 95 | /** @phpstan-ignore method.notFound */ 96 | && $type->getClassStringObjectType()->isObject()->yes() => $type->getClassStringObjectType()->getClassName(), 97 | default => null, 98 | }; 99 | 100 | if (! is_string($classString)) { 101 | return null; 102 | } 103 | 104 | return $this->refactorClosure($node, $functionLike, $classString); 105 | } 106 | 107 | public function refactorClosure(StaticCall $staticCall, Closure|ArrowFunction $closure, string $class): StaticCall 108 | { 109 | $closure->params[0]->type = new FullyQualified($class); 110 | 111 | $staticCall->args = [ 112 | new Arg($closure), 113 | ]; 114 | 115 | return $staticCall; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Rector/StaticCall/CarbonSetTestNowToTravelToRector.php: -------------------------------------------------------------------------------- 1 | travelTo()` method in Laravel\'s `TestCase` class instead of the `Carbon::setTestNow()` method.', 32 | [ 33 | new CodeSample( 34 | <<<'CODE_SAMPLE' 35 | use Illuminate\Support\Carbon; 36 | use Illuminate\Foundation\Testing\TestCase; 37 | 38 | class SomeTest extends TestCase 39 | { 40 | public function test() 41 | { 42 | Carbon::setTestNow('2024-08-11'); 43 | } 44 | } 45 | CODE_SAMPLE 46 | , 47 | <<<'CODE_SAMPLE' 48 | use Illuminate\Support\Carbon; 49 | use Illuminate\Foundation\Testing\TestCase; 50 | 51 | class SomeTest extends TestCase 52 | { 53 | public function test() 54 | { 55 | $this->travelTo('2024-08-11'); 56 | } 57 | } 58 | CODE_SAMPLE 59 | ), 60 | ], 61 | ); 62 | } 63 | 64 | /** 65 | * @return array> 66 | */ 67 | public function getNodeTypes(): array 68 | { 69 | return [StaticCall::class]; 70 | } 71 | 72 | public function refactor(Node $node): ?MethodCall 73 | { 74 | if (! $node instanceof StaticCall) { 75 | return null; 76 | } 77 | 78 | $scope = ScopeFetcher::fetch($node); 79 | 80 | if (! $scope->isInClass()) { 81 | return null; 82 | } 83 | 84 | if (! $scope->getClassReflection()->isSubclassOfClass($this->reflectionProvider->getClass('Illuminate\Foundation\Testing\TestCase'))) { 85 | return null; 86 | } 87 | 88 | if (! $this->isName($node->name, 'setTestNow')) { 89 | return null; 90 | } 91 | 92 | if (! $this->isCarbon($node->class)) { 93 | return null; 94 | } 95 | 96 | $args = $node->args === [] 97 | ? [new Arg($this->nodeFactory->createNull())] 98 | : $node->args; 99 | 100 | return $this->nodeFactory->createMethodCall( 101 | new Variable('this'), 102 | 'travelTo', 103 | $args, 104 | ); 105 | } 106 | 107 | private function isCarbon(Node $node): bool 108 | { 109 | return $this->isObjectType($node, new ObjectType('Carbon\Carbon')) || 110 | $this->isObjectType($node, new ObjectType('Carbon\CarbonImmutable')) || 111 | $this->isObjectType($node, new ObjectType('Illuminate\Support\Carbon')); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Rector/StaticCall/Redirect301ToPermanentRedirectRector.php: -------------------------------------------------------------------------------- 1 | routerObjectTypes = [ 33 | new ObjectType('Illuminate\Support\Facades\Route'), 34 | new ObjectType('Illuminate\Routing\Route'), 35 | ]; 36 | } 37 | 38 | public function getRuleDefinition(): RuleDefinition 39 | { 40 | return new RuleDefinition( 41 | 'Change "redirect" call with 301 to "permanentRedirect"', 42 | [ 43 | new CodeSample( 44 | <<<'CODE_SAMPLE' 45 | class SomeClass 46 | { 47 | public function run() 48 | { 49 | Illuminate\Routing\Route::redirect('/foo', '/bar', 301); 50 | } 51 | } 52 | CODE_SAMPLE 53 | , 54 | <<<'CODE_SAMPLE' 55 | class SomeClass 56 | { 57 | public function run() 58 | { 59 | Illuminate\Routing\Route::permanentRedirect('/foo', '/bar'); 60 | } 61 | } 62 | CODE_SAMPLE 63 | ), 64 | ] 65 | ); 66 | } 67 | 68 | /** 69 | * @return array> 70 | */ 71 | public function getNodeTypes(): array 72 | { 73 | return [StaticCall::class]; 74 | } 75 | 76 | /** 77 | * @param StaticCall $node 78 | */ 79 | public function refactor(Node $node): ?Node 80 | { 81 | if (! $this->nodeTypeResolver->isObjectTypes($node->class, $this->routerObjectTypes)) { 82 | return null; 83 | } 84 | 85 | if (! isset($node->args[2])) { 86 | return null; 87 | } 88 | 89 | if (! $node->args[2] instanceof Arg) { 90 | return null; 91 | } 92 | 93 | $is301 = $this->valueResolver->isValue($node->args[2]->value, 301); 94 | if (! $is301) { 95 | return null; 96 | } 97 | 98 | unset($node->args[2]); 99 | 100 | $node->name = new Identifier('permanentRedirect'); 101 | 102 | return $node; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Rector/StaticCall/ReplaceAssertTimesSendWithAssertSentTimesRector.php: -------------------------------------------------------------------------------- 1 | isObjectType( 44 | $node->class, 45 | new ObjectType('Illuminate\Support\Facades\Notification') 46 | )) { 47 | return null; 48 | } 49 | 50 | if ($this->getName($node->name) !== 'assertTimesSent') { 51 | return null; 52 | } 53 | 54 | $node->name = new Identifier('assertSentTimes'); 55 | 56 | $node->args = array_reverse($node->args); 57 | 58 | return $node; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Set/LaravelLevelSetList.php: -------------------------------------------------------------------------------- 1 | class); 21 | } 22 | 23 | public function getMethod(): string 24 | { 25 | return $this->method; 26 | } 27 | 28 | public function getPosition(): int 29 | { 30 | return $this->position; 31 | } 32 | 33 | public function getDefaultValue(): mixed 34 | { 35 | return $this->defaultValue; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ValueObject/ApplyDefaultInsteadOfNullCoalesce.php: -------------------------------------------------------------------------------- 1 | objectType; 14 | } 15 | 16 | public function getMethodName(): string 17 | { 18 | return $this->methodName; 19 | } 20 | 21 | public function getArgumentPosition(): int 22 | { 23 | return $this->argumentPosition; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ValueObject/ArgumentFuncCallToMethodCall.php: -------------------------------------------------------------------------------- 1 | function; 25 | } 26 | 27 | public function getClass(): string 28 | { 29 | return $this->class; 30 | } 31 | 32 | public function getMethodIfNoArgs(): ?string 33 | { 34 | return $this->methodIfNoArgs; 35 | } 36 | 37 | public function getMethodIfArgs(): ?string 38 | { 39 | return $this->methodIfArgs; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ValueObject/ArrayFuncCallToMethodCall.php: -------------------------------------------------------------------------------- 1 | function; 33 | } 34 | 35 | public function getClass(): string 36 | { 37 | return $this->class; 38 | } 39 | 40 | public function getArrayMethod(): string 41 | { 42 | return $this->arrayMethod; 43 | } 44 | 45 | public function getNonArrayMethod(): string 46 | { 47 | return $this->nonArrayMethod; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ValueObject/ExpectedClassMethodMethodCalls.php: -------------------------------------------------------------------------------- 1 | $expectedItems 15 | * @param MethodCall[] $notExpectedMethodCalls 16 | * @param list $notExpectedItems 17 | */ 18 | public function __construct( 19 | private array $expectedMethodCalls = [], 20 | private array $expectedItems = [], 21 | private array $notExpectedMethodCalls = [], 22 | private array $notExpectedItems = [] 23 | ) { 24 | Assert::allIsInstanceOf($this->expectedMethodCalls, MethodCall::class); 25 | Assert::allIsInstanceOf($this->notExpectedMethodCalls, MethodCall::class); 26 | Assert::allIsInstanceOfAny($this->expectedItems, [String_::class, ClassConstFetch::class]); 27 | Assert::allIsInstanceOfAny($this->expectedItems, [String_::class, ClassConstFetch::class]); 28 | } 29 | 30 | public function isActionable(): bool 31 | { 32 | return ! ($this->expectedMethodCalls === [] && $this->notExpectedMethodCalls === []); 33 | } 34 | 35 | /** 36 | * @return MethodCall[] 37 | */ 38 | public function getAllMethodCalls(): array 39 | { 40 | return array_merge($this->expectedMethodCalls, $this->notExpectedMethodCalls); 41 | } 42 | 43 | /** 44 | * @return MethodCall[] 45 | */ 46 | public function getNotExpectedMethodCalls(): array 47 | { 48 | return $this->notExpectedMethodCalls; 49 | } 50 | 51 | /** 52 | * @return array, ClassConstFetch|String_> 53 | */ 54 | public function getItemsToFake(): array 55 | { 56 | return array_unique(array_merge($this->expectedItems, $this->notExpectedItems), SORT_REGULAR); 57 | } 58 | 59 | /** 60 | * @return array, ClassConstFetch|String_> 61 | */ 62 | public function getExpectedItems(): array 63 | { 64 | return array_unique($this->expectedItems, SORT_REGULAR); 65 | } 66 | 67 | /** 68 | * @return array, ClassConstFetch|String_> 69 | */ 70 | public function getNotExpectedItems(): array 71 | { 72 | return array_unique($this->notExpectedItems, SORT_REGULAR); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/ValueObject/ReplaceRequestKeyAndMethodValue.php: -------------------------------------------------------------------------------- 1 | method, ['query', 'post', 'input']); 12 | } 13 | 14 | public function getKey(): string 15 | { 16 | return $this->key; 17 | } 18 | 19 | public function getMethod(): string 20 | { 21 | return $this->method; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ValueObject/ReplaceServiceContainerCallArg.php: -------------------------------------------------------------------------------- 1 | oldService; 17 | } 18 | 19 | public function getNewService(): string|ClassConstFetch 20 | { 21 | return $this->newService; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ValueObject/ServiceNameTypeAndVariableName.php: -------------------------------------------------------------------------------- 1 | serviceName; 18 | } 19 | 20 | public function getType(): string 21 | { 22 | return $this->type; 23 | } 24 | 25 | public function getVariableName(): string 26 | { 27 | return $this->variableName; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ValueObject/TypeToTimeMethodAndPosition.php: -------------------------------------------------------------------------------- 1 | type); 20 | } 21 | 22 | public function getMethodName(): string 23 | { 24 | return $this->methodName; 25 | } 26 | 27 | public function getPosition(): int 28 | { 29 | return $this->position; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /templates/configurable-config.php.template: -------------------------------------------------------------------------------- 1 | ruleWithConfiguration(__NAME__::class, ['option' => 'value']); 10 | }; 11 | -------------------------------------------------------------------------------- /templates/configurable-rule.php.template: -------------------------------------------------------------------------------- 1 | 'value'] 32 | )] 33 | ); 34 | } 35 | 36 | /** 37 | * @return array> 38 | */ 39 | public function getNodeTypes(): array 40 | { 41 | // @todo select node type 42 | return [Class_::class]; 43 | } 44 | 45 | /** 46 | * @param mixed[] $configuration 47 | */ 48 | public function configure(array $configuration): void 49 | { 50 | // Add configuration logic here 51 | } 52 | 53 | /** 54 | * @param Class_ $node 55 | */ 56 | public function refactor(Node $node): ?Node 57 | { 58 | // @todo change the node 59 | 60 | return $node; 61 | } 62 | } -------------------------------------------------------------------------------- /templates/fixture.php.inc.template: -------------------------------------------------------------------------------- 1 | 11 | ----- 12 | 22 | -------------------------------------------------------------------------------- /templates/non-configurable-config.php.template: -------------------------------------------------------------------------------- 1 | rule(__NAME__::class); 10 | }; 11 | -------------------------------------------------------------------------------- /templates/non-configurable-rule.php.template: -------------------------------------------------------------------------------- 1 | > 35 | */ 36 | public function getNodeTypes(): array 37 | { 38 | // @todo select node type 39 | return [Class_::class]; 40 | } 41 | 42 | /** 43 | * @param Class_ $node 44 | */ 45 | public function refactor(Node $node): ?Node 46 | { 47 | // @todo change the node 48 | 49 | return $node; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /templates/test.php.template: -------------------------------------------------------------------------------- 1 | doTestFile($filePath); 25 | } 26 | 27 | public function provideConfigFilePath(): string 28 | { 29 | return __DIR__ . '/config/configured_rule.php'; 30 | } 31 | } 32 | --------------------------------------------------------------------------------