diff --git a/.github/workflows/infection.yml b/.github/workflows/psalm.yml similarity index 57% rename from .github/workflows/infection.yml rename to .github/workflows/psalm.yml index 0d39d5867..03eb1cd76 100644 --- a/.github/workflows/infection.yml +++ b/.github/workflows/psalm.yml @@ -1,4 +1,4 @@ -name: Infection +name: Psalm on: pull_request: @@ -7,43 +7,41 @@ on: paths: - '**.php' - 'composer.*' - - 'phpunit*' - - '.github/workflows/infection.yml' + - 'psalm*' + - '.github/workflows/psalm.yml' push: branches: - develop paths: - '**.php' - 'composer.*' - - 'phpunit*' - - '.github/workflows/infection.yml' + - 'psalm*' + - '.github/workflows/psalm.yml' jobs: - main: - name: Mutation Testing + build: + name: PHP ${{ matrix.php-versions }} Psalm Analysis runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[ci skip]')" + strategy: + fail-fast: false + matrix: + php-versions: ['7.4', '8.0', '8.1'] steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up PHP + - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' - tools: infection, phpunit - extensions: intl, json, mbstring, gd, xml, sqlite3 - coverage: xdebug + php-version: ${{ matrix.php-versions }} + tools: phpstan, phpunit + extensions: intl, json, mbstring, xml + coverage: none env: COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set up problem matchers for PHPUnit - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - - name: Configure matchers - uses: mheap/phpunit-matcher-action@v1 - - name: Get composer cache directory id: composer-cache run: echo "::set-output name=dir::$(composer config cache-files-dir)" @@ -55,6 +53,16 @@ jobs: key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ hashFiles('**/composer.lock') }} restore-keys: ${{ runner.os }}-composer- + - name: Create Psalm cache directory + run: mkdir -p build/psalm + + - name: Cache Psalm results + uses: actions/cache@v3 + with: + path: build/psalm + key: ${{ runner.os }}-psalm-${{ github.sha }} + restore-keys: ${{ runner.os }}-psalm- + - name: Install dependencies run: | if [ -f composer.lock ]; then @@ -63,10 +71,5 @@ jobs: composer update --no-progress --no-interaction --prefer-dist --optimize-autoloader fi - - name: Test with PHPUnit - run: vendor/bin/phpunit --teamcity - - - name: Mutate with Infection - run: | - git fetch --depth=1 origin $GITHUB_BASE_REF - infection --threads=2 --skip-initial-tests --coverage=build/phpunit --git-diff-base=origin/$GITHUB_BASE_REF --git-diff-filter=AM --logger-github --ignore-msi-with-no-mutations + - name: Run Psalm analysis + run: vendor/bin/psalm diff --git a/composer.json b/composer.json index ba89a06ca..344fbff91 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,14 @@ { "name": "codeigniter4/shield", - "type": "library", "description": "Authentication and Authorization for CodeIgniter 4", + "license": "MIT", + "type": "library", "keywords": [ "codeigniter", "codeigniter4", "authentication", "authorization" ], - "homepage": "https://github.com/codeigniter4/shield", - "license": "MIT", "authors": [ { "name": "Lonnie Ezell", @@ -17,31 +16,28 @@ "role": "Developer" } ], + "homepage": "https://github.com/codeigniter4/shield", "require": { "php": "^7.4 || ^8.0", "codeigniter4/settings": "^2.0" }, - "provide": { - "codeigniter4/authentication-implementation": "1.0" - }, "require-dev": { - "codeigniter/coding-standard": "^1.1", + "codeigniter4/devkit": "^1.0", "codeigniter4/framework": "^4.1", - "fakerphp/faker": "^1.9", - "friendsofphp/php-cs-fixer": "^3.0", "mockery/mockery": "^1.0", - "nexusphp/cs-config": "^3.1", - "nexusphp/tachycardia": "^1.0", - "php-coveralls/php-coveralls": "^2.4", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^9.1", "vimeo/psalm": "^4.22" }, - "config": { - "allow-plugins": { - "phpstan/extension-installer": true - } + "provide": { + "codeigniter4/authentication-implementation": "1.0" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/codeigniter4/CodeIgniter4" + } + ], + "minimum-stability": "dev", + "prefer-stable": true, "autoload": { "psr-4": { "CodeIgniter\\Shield\\": "src" @@ -56,23 +52,16 @@ "Tests\\Support\\": "tests/_support" } }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/codeigniter4/CodeIgniter4" + "config": { + "allow-plugins": { + "phpstan/extension-installer": true } - ], - "minimum-stability": "dev", - "prefer-stable": true, + }, "scripts": { "post-update-cmd": [ "bash admin/setup.sh" ], "analyze": "phpstan analyze", - "sa": [ - "@analyze", - "psalm" - ], "ci": [ "Composer\\Config::disableProcessTimeout", "@deduplicate", @@ -82,11 +71,15 @@ "rector process", "@style" ], + "cs-fix": "@style", "deduplicate": "phpcpd app/ src/", "inspect": "deptrac analyze --cache-file=build/deptrac.cache", "mutate": "infection --threads=2 --skip-initial-tests --coverage=build/phpunit", - "style": "php-cs-fixer fix --ansi --verbose --diff", - "cs-fix": "@style", + "sa": [ + "phpstan analyze", + "psalm" + ], + "style": "php-cs-fixer fix --verbose --ansi --using-cache=no", "test": "phpunit" } } diff --git a/psalm.xml b/psalm.xml index e94600546..28656c022 100644 --- a/psalm.xml +++ b/psalm.xml @@ -6,9 +6,11 @@ xmlns="https://getpsalm.org/schema/config" xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" autoloader="psalm_autoload.php" + cacheDirectory="build/psalm/" > - + + diff --git a/rector.php b/rector.php index 7d81cb1d7..92da2f5a3 100644 --- a/rector.php +++ b/rector.php @@ -1,5 +1,6 @@ import(SetList::DEAD_CODE); - $containerConfigurator->import(LevelSetList::UP_TO_PHP_74); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD); - $containerConfigurator->import(PHPUnitSetList::PHPUNIT_80); - - $parameters = $containerConfigurator->parameters(); - - $parameters->set(Option::PARALLEL, true); +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->sets([SetList::DEAD_CODE, LevelSetList::UP_TO_PHP_74, PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD, PHPUnitSetList::PHPUNIT_80]); + $rectorConfig->parallel(); // The paths to refactor (can also be supplied with CLI arguments) - $parameters->set(Option::PATHS, [ + $rectorConfig->paths([ __DIR__ . '/src/', __DIR__ . '/tests/', ]); // Include Composer's autoload - required for global execution, remove if running locally - $parameters->set(Option::AUTOLOAD_PATHS, [ + $rectorConfig->autoloadPaths([ __DIR__ . '/vendor/autoload.php', ]); // Do you need to include constants, class aliases, or a custom autoloader? - $parameters->set(Option::BOOTSTRAP_FILES, [ + $rectorConfig->bootstrapFiles([ realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php', ]); if (is_file(__DIR__ . '/phpstan.neon.dist')) { - $parameters->set(Option::PHPSTAN_FOR_RECTOR_PATH, __DIR__ . '/phpstan.neon.dist'); + $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist'); } // Set the target version for refactoring - $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_74); + $rectorConfig->phpVersion(PhpVersion::PHP_74); // Auto-import fully qualified class names - $parameters->set(Option::AUTO_IMPORT_NAMES, true); + $rectorConfig->importNames(); // Are there files or rules you need to skip? - $parameters->set(Option::SKIP, [ - __DIR__ . '/src/Config/Auth.php', + $rectorConfig->skip([ __DIR__ . '/src/Views', JsonThrowOnErrorRector::class, @@ -88,7 +79,7 @@ __DIR__ . '/tests', ], - // Ignore files that should not be namespaced + // Ignore files that should not be namespaced to their folder NormalizeNamespaceByPSR4ComposerAutoloadRector::class => [ __DIR__ . '/src/Helpers', __DIR__ . '/tests/_support', @@ -100,33 +91,30 @@ // May be uninitialized on purpose AddDefaultValueForUndefinedVariableRector::class, ]); - - // Additional rules to apply - $services = $containerConfigurator->services(); - $services->set(SimplifyUselessVariableRector::class); - $services->set(RemoveAlwaysElseRector::class); - $services->set(CountArrayToEmptyArrayComparisonRector::class); - $services->set(ForToForeachRector::class); - $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); - $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); - $services->set(SimplifyStrposLowerRector::class); - $services->set(CombineIfRector::class); - $services->set(SimplifyIfReturnBoolRector::class); - $services->set(InlineIfToExplicitIfRector::class); - $services->set(PreparedValueToEarlyReturnRector::class); - $services->set(ShortenElseIfRector::class); - $services->set(SimplifyIfElseToTernaryRector::class); - $services->set(UnusedForeachValueToArrayKeysRector::class); - $services->set(ChangeArrayPushToArrayAssignRector::class); - $services->set(UnnecessaryTernaryExpressionRector::class); - $services->set(AddPregQuoteDelimiterRector::class); - $services->set(SimplifyRegexPatternRector::class); - $services->set(FuncGetArgsToVariadicParamRector::class); - $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); - $services->set(SimplifyEmptyArrayCheckRector::class); - $services->set(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); - $services->set(TypedPropertyRector::class) - ->configure([ - TypedPropertyRector::INLINE_PUBLIC => true, + $rectorConfig->rule(SimplifyUselessVariableRector::class); + $rectorConfig->rule(RemoveAlwaysElseRector::class); + $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class); + $rectorConfig->rule(ForToForeachRector::class); + $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class); + $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class); + $rectorConfig->rule(SimplifyStrposLowerRector::class); + $rectorConfig->rule(CombineIfRector::class); + $rectorConfig->rule(SimplifyIfReturnBoolRector::class); + $rectorConfig->rule(InlineIfToExplicitIfRector::class); + $rectorConfig->rule(PreparedValueToEarlyReturnRector::class); + $rectorConfig->rule(ShortenElseIfRector::class); + $rectorConfig->rule(SimplifyIfElseToTernaryRector::class); + $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class); + $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class); + $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class); + $rectorConfig->rule(AddPregQuoteDelimiterRector::class); + $rectorConfig->rule(SimplifyRegexPatternRector::class); + $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class); + $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class); + $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class); + $rectorConfig->rule(NormalizeNamespaceByPSR4ComposerAutoloadRector::class); + $rectorConfig + ->ruleWithConfiguration(TypedPropertyRector::class, [ + TypedPropertyRector::INLINE_PUBLIC => false, ]); }; diff --git a/src/Config/Auth.php b/src/Config/Auth.php index 46c08fc3f..9a710bc6c 100644 --- a/src/Config/Auth.php +++ b/src/Config/Auth.php @@ -8,6 +8,7 @@ use CodeIgniter\Shield\Authentication\Authenticators\AccessTokens; use CodeIgniter\Shield\Authentication\Authenticators\Session; use CodeIgniter\Shield\Authentication\Passwords\ValidatorInterface; +use CodeIgniter\Shield\Models\UserModel; class Auth extends BaseConfig { @@ -324,7 +325,6 @@ class Auth extends BaseConfig * OTHER SETTINGS * //////////////////////////////////////////////////////////////////// */ - /** * -------------------------------------------------------------------- * User Provider @@ -335,7 +335,7 @@ class Auth extends BaseConfig * You can change it as long as they adhere to the * CodeIgniter\Shield\Models\UserModel. * - * @var class-string<\CodeIgniter\Shield\Models\UserModel> + * @var class-string */ public string $userProvider = 'CodeIgniter\Shield\Models\UserModel'; diff --git a/tests/Authentication/HasAccessTokensTest.php b/tests/Authentication/HasAccessTokensTest.php index 07fab5b43..0ea8c2437 100644 --- a/tests/Authentication/HasAccessTokensTest.php +++ b/tests/Authentication/HasAccessTokensTest.php @@ -55,6 +55,7 @@ public function testAccessTokens() ['user_id' => $this->user->id, 'type' => 'access_token', 'secret' => 'secretToken2'] ); + /** @var AccessToken[] $tokens */ $tokens = $this->user->accessTokens(); $this->assertCount(2, $tokens); diff --git a/tests/Unit/UserTest.php b/tests/Unit/UserTest.php index b9469cf02..f27de075e 100644 --- a/tests/Unit/UserTest.php +++ b/tests/Unit/UserTest.php @@ -87,7 +87,7 @@ public function testLastLogin() $last = $this->user->lastLogin(); - $this->assertInstanceOf(Login::class, $last); + $this->assertInstanceOf(Login::class, $last); // @phpstan-ignore-line $this->assertSame($login->id, $last->id); $this->assertInstanceOf(Time::class, $last->date); }