Skip to content

Commit

Permalink
feat(phpstan): Update phpstan to max level + use our conventions
Browse files Browse the repository at this point in the history
- use our reusable larastrict/conventions package

BC: some expectations now contains correct typehint
  • Loading branch information
pionl committed Apr 7, 2024
1 parent c2f8bc0 commit 2b49efa
Show file tree
Hide file tree
Showing 106 changed files with 389 additions and 313 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
name: "Run tests"
strategy:
matrix:
phpVersion: [ "8.1", "8.2" ]
phpVersion: [ "8.1", "8.2", "8.3" ]
uses: wrk-flow/reusable-workflows/.github/workflows/php-tests.yml@7562253bdc3769847417e3476b501e7126f5d2c0
with:
gistID: ${{ vars.GIST_ID }}
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,21 @@ Want more tools or want to help? Check [wrk-flow.com](https://wrk-flow.com) or [
- Make caching more reusable and testable -> `CacheMeService`
- Move business logic to `Actions` or `Services`
- Setup Laravel for great docker experience.

## Acknowledgement

This project owes its existence to the generous support of several other impactful projects:

- **[Canvastera](https://canvastera.com)** - Empowering users to craft multimedia posters and share them worldwide. (EDU/Hobby)
- **[Azzurro Travel Agency](https://azzurro.cz)** - Specializing in holidays in Italy.
- **[Redtag Studio](https://redtag.studio)** - Crafting digital products for your enjoyment.

Explore more of our open-source initiatives:

- **[Larastrict](https://larastrict.com)** - Enhancing the Laravel Framework with a suite of convenient tools and packages.
- **[StrictPHP](https://strictphp.com)** - Enabling strictness in PHP projects through a curated set of packages and conventions, fostering the development of robust production-grade applications.
- **[WrkFlow](https://wrk-flow.com)** - Streamlining development workflows with a comprehensive set of tools designed to boost efficiency.

## License

Open-source software licensed under the [MIT License](LICENSE.md). Feel free to use and modify it according to your needs.
46 changes: 29 additions & 17 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
"name": "wrkflow/larastrict",
"type": "project",
"description": "Package to improve Laravel framework with features to support more strict typed Laravel.",
"keywords": ["framework", "laravel", "php", "strictphp", "strict"],
"keywords": [
"framework",
"laravel",
"php",
"strictphp",
"strict"
],
"license": "MIT",
"homepage": "https://github.com/wrk-flow/larastrict",
"authors": [
Expand All @@ -18,31 +24,34 @@
"psr/simple-cache": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.5.0",
"nette/php-generator": "v4.0.5",
"nikic/php-parser": "v4.15.2",
"nunomaduro/larastan": "2.2.9",
"orchestra/testbench": "^v7.13.0",
"phpstan/phpdoc-parser": "^1.13",
"phpstan/phpstan": "1.9.4",
"phpstan/phpstan-deprecation-rules": "^1.0.0",
"larastrict/conventions": "v0.1.0",
"mockery/mockery": "^1.6",
"nette/php-generator": "^v4.1",
"orchestra/testbench": "^v7",
"phpstan/phpstan-mockery": "^1.1.0",
"phpstan/phpstan-phpunit": "^1.1.1",
"phpunit/phpunit": "^9.5.27",
"rector/rector": "0.15.1",
"symplify/easy-coding-standard": "11.1.20"
"phpstan/phpdoc-parser": "^1.13",
"nikic/php-parser": "^v4.15.2"
},
"scripts": {
"post-autoload-dump": [
"@php ./vendor/bin/testbench package:discover --ansi"
],
"check": "composer lint && composer test && composer lint:stan",
"analyse": "./vendor/bin/phpstan",
"check": [
"@lint",
"@test",
"@analyse"
],
"lint": [
"@lint:fix",
"@lint:upgrade"
],
"lint:check": "./vendor/bin/ecs",
"lint:fix": "./vendor/bin/ecs --fix",
"lint:stan": "./vendor/bin/phpstan",
"lint:upgrade:check": "vendor/bin/rector process --dry-run",
"lint:upgrade": "vendor/bin/rector process",
"lint": "composer lint:upgrade && composer lint:fix && composer lint:stan",
"lint:upgrade:check": "vendor/bin/rector process --dry-run",
"lint:stan:b": "./vendor/bin/phpstan -b",
"test": "./vendor/bin/phpunit",
"test:stubs": "STUBS_GENERATE=true ./vendor/bin/phpunit",
"test:coverage": "./vendor/bin/phpunit --coverage-text"
Expand Down Expand Up @@ -75,7 +84,10 @@
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"phpstan/extension-installer": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
Expand Down
28 changes: 7 additions & 21 deletions ecs.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,13 @@
use Symplify\EasyCodingStandard\Config\ECSConfig;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;

return static function (ECSConfig $containerConfigurator): void {
$containerConfigurator->import(SetList::PSR_12);
$containerConfigurator->import(SetList::SYMPLIFY);
$containerConfigurator->import(SetList::COMMON);
$containerConfigurator->import(SetList::CLEAN_CODE);

$containerConfigurator->ruleWithConfiguration(ClassAttributesSeparationFixer::class, [
'elements' => [
'const' => 'only_if_meta',
'property' => 'one',
'method' => 'one',
],
]);

$containerConfigurator->parallel();
$containerConfigurator->paths(
[__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/ecs.php', __DIR__ . '/rector.php']
);
$containerConfigurator->skip([
YodaStyleFixer::class,
return ECSConfig::configure()
->withRootFiles()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
->withSets([
__DIR__ . '/vendor/larastrict/conventions/extension-ecs.php'
])
->withSkip([
// We want to leave the relative constant path usage
__DIR__ . '/tests/Feature/Testing/Commands/MakeExpectationCommand/*.php',
]);
};
55 changes: 55 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\$createState of method LaraStrict\\\\Context\\\\Services\\\\ContextService\\:\\:get\\(\\) expects Closure\\(mixed, mixed, mixed, mixed, mixed, mixed\\)\\: LaraStrict\\\\Context\\\\Values\\\\BoolContextValue, Closure\\(Illuminate\\\\Contracts\\\\Container\\\\Container\\)\\: LaraStrict\\\\Context\\\\Values\\\\BoolContextValue given\\.$#"
count: 1
path: src/Context/Services/ContextService.php

-
message: "#^Parameter \\#1 \\$ of closure expects TModel of Illuminate\\\\Database\\\\Eloquent\\\\Model, mixed given\\.$#"
count: 1
path: src/Database/Queries/ChunkedModelQueryResult.php

-
message: "#^Parameter \\#1 \\$ of closure expects array\\<int\\|string\\>, array\\<int\\<0, max\\>, mixed\\> given\\.$#"
count: 1
path: src/Database/Queries/ChunkedModelQueryResult.php

-
message: "#^Parameter \\#2 \\$callback of method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\<Illuminate\\\\Database\\\\Eloquent\\\\Model\\>\\:\\:chunk\\(\\) expects callable\\(Illuminate\\\\Database\\\\Eloquent\\\\Collection\\<int, Illuminate\\\\Database\\\\Eloquent\\\\Model\\>, int\\)\\: mixed, Closure\\(Illuminate\\\\Database\\\\Eloquent\\\\Collection\\<int, TModel\\>\\)\\: void given\\.$#"
count: 1
path: src/Database/Queries/ChunkedModelQueryResult.php

-
message: "#^Access to an undefined property LaraStrict\\\\Http\\\\Resources\\\\JsonResource\\:\\:\\$preserveKeys\\.$#"
count: 1
Expand All @@ -15,6 +35,16 @@ parameters:
count: 1
path: src/Http/Resources/JsonResourceCollection.php

-
message: "#^Method LaraStrict\\\\Log\\\\Channels\\\\ConsoleOutputChannel\\:\\:getLevel\\(\\) should return 100\\|200\\|250\\|300\\|400\\|500\\|550\\|600\\|'ALERT'\\|'alert'\\|'CRITICAL'\\|'critical'\\|'DEBUG'\\|'debug'\\|'EMERGENCY'\\|'emergency'\\|'ERROR'\\|'error'\\|'INFO'\\|'info'\\|'NOTICE'\\|'notice'\\|'WARNING'\\|'warning' but returns int\\|string\\.$#"
count: 1
path: src/Log/Channels/ConsoleOutputChannel.php

-
message: "#^Parameter \\$getValue of method LaraStrict\\\\Cache\\\\Contracts\\\\CacheMeServiceContract\\:\\:get\\(\\) expects Closure\\(mixed, mixed, mixed, mixed, mixed, mixed\\)\\: LaraStrict\\\\Providers\\\\Entities\\\\AppServiceProviderEntity, Closure\\(Illuminate\\\\Contracts\\\\Foundation\\\\Application\\)\\: LaraStrict\\\\Providers\\\\Entities\\\\AppServiceProviderEntity given\\.$#"
count: 1
path: src/Providers/Actions/GetAppServiceProviderForClassAction.php

-
message: "#^Cannot cast array\\|string to string\\.$#"
count: 1
Expand All @@ -25,12 +55,37 @@ parameters:
count: 1
path: src/Testing/Actions/ParsePhpDocAction.php

-
message: "#^Method Tests\\\\LaraStrict\\\\Feature\\\\Context\\\\Services\\\\IsContext\\:\\:is\\(\\) should return Closure\\(mixed, mixed, mixed, mixed, mixed\\)\\: bool but returns Closure\\(bool\\)\\: bool\\.$#"
count: 1
path: tests/Feature/Context/Services/IsContext.php

-
message: "#^Parameter \\$createState of method LaraStrict\\\\Context\\\\Contracts\\\\ContextServiceContract\\:\\:get\\(\\) expects Closure\\(mixed, mixed, mixed, mixed, mixed, mixed\\)\\: Tests\\\\LaraStrict\\\\Feature\\\\Context\\\\Services\\\\TestValue, Closure\\(string\\)\\: Tests\\\\LaraStrict\\\\Feature\\\\Context\\\\Services\\\\TestValue given\\.$#"
count: 1
path: tests/Feature/Context/Services/TestDependencyContext.php

-
message: "#^Call to method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false and 'Hook should be…' will always evaluate to false\\.$#"
count: 1
path: tests/Feature/Testing/Commands/MakeExpectationCommandRealTest.php

-
message: "#^Call to method LaraStrict\\\\Testing\\\\Laravel\\\\Contracts\\\\View\\\\FactoryAssert\\:\\:exists\\(\\) with 'test' will always evaluate to false\\.$#"
count: 2
path: tests/Feature/Testing/Laravel/Contracts/View/FactoryAssertTest.php

-
message: "#^Method Tests\\\\LaraStrict\\\\Feature\\\\Translations\\\\InvalidServiceProviderTranslations\\:\\:getProviderClass\\(\\) should return class\\-string\\<LaraStrict\\\\Providers\\\\AbstractServiceProvider\\> but returns string\\.$#"
count: 1
path: tests/Feature/Translations/InvalidServiceProviderTranslations.php

-
message: "#^Parameter \\$createState of method LaraStrict\\\\Testing\\\\Context\\\\Contracts\\\\ContextServiceContractAssert\\:\\:get\\(\\) expects Closure\\(mixed, mixed, mixed, mixed, mixed, mixed\\)\\: Tests\\\\LaraStrict\\\\Feature\\\\Context\\\\Services\\\\TestValue, Closure\\(string\\)\\: Tests\\\\LaraStrict\\\\Feature\\\\Context\\\\Services\\\\TestValue given\\.$#"
count: 2
path: tests/Unit/Testing/Context/Contracts/ContextServiceContractAssertTest.php

-
message: "#^Parameter \\$is of method LaraStrict\\\\Testing\\\\Context\\\\Contracts\\\\ContextServiceContractAssert\\:\\:is\\(\\) expects Closure\\(mixed, mixed, mixed, mixed, mixed, mixed\\)\\: bool, Closure\\(string\\)\\: true given\\.$#"
count: 1
path: tests/Unit/Testing/Context/Contracts/ContextServiceContractAssertTest.php
25 changes: 3 additions & 22 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,35 +1,16 @@
includes:
- vendor/nunomaduro/larastan/extension.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
- vendor/phpstan/phpstan-mockery/extension.neon
- phpstan-baseline.neon

parameters:
reportUnmatchedIgnoredErrors: true
level: max

parallel:
processTimeout: 600.0
# Due the tests between Laravel versions
reportUnmatchedIgnoredErrors: true

paths:
- src
- tests

# The level 8 is the highest level
level: 8

# it is impossible to map toArray()
checkMissingIterableValueType: false

excludePaths:
- 'tests/Feature/Testing/Commands/MakeExpectationCommand/*.php'

ignoreErrors:
# CastsAttributes template was added in Laravel 9.49 (remove when we drop L9 support)
-
message: "#^PHPDoc tag @implements contains generic type Illuminate\\\\Contracts\\\\Database\\\\Eloquent\\\\CastsAttributes\\<float, float\\|int\\|string\\|null\\> but interface Illuminate\\\\Contracts\\\\Database\\\\Eloquent\\\\CastsAttributes is not generic\\.$#"
count: 1
path: src/Database/Models/Casts/FloatCast.php
reportUnmatched: false

48 changes: 11 additions & 37 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,22 @@

declare(strict_types=1);

use Rector\CodeQuality\Rector\ClassMethod\ReturnTypeFromStrictScalarReturnExprRector;
use Rector\CodingStyle\Rector\ArrowFunction\StaticArrowFunctionRector;
use Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector;
use Rector\CodingStyle\Rector\ClassMethod\UnSpreadOperatorRector;
use Rector\CodingStyle\Rector\Stmt\NewlineAfterStatementRector;
use Rector\Config\RectorConfig;
use Rector\Core\ValueObject\PhpVersion;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
use Rector\Strict\Rector\AbstractFalsyScalarRuleFixerRector;
use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector;

return static function (RectorConfig $config): void {
$config->paths([__DIR__ . '/src', __DIR__ . '/tests']);
$config->phpVersion(PhpVersion::PHP_81);
// SKIP laravel
$laravelClasses = [
__DIR__ . '/src/Testing/Laravel/TestingApplication.php',
__DIR__ . '/src/Testing/Laravel/TestingContainer.php',
__DIR__ . '/src/Testing/Laravel/Filesystem/Filesystem.php',
];

// Define what rule sets will be applied
$config->import(LevelSetList::UP_TO_PHP_81);
$config->import(SetList::CODE_QUALITY);
$config->import(SetList::CODING_STYLE);
$config->importNames();

$config->ruleWithConfiguration(
BooleanInBooleanNotRuleFixerRector::class,
[
AbstractFalsyScalarRuleFixerRector::TREAT_AS_NON_EMPTY => false,
]
);

// SKIP laravel
$laravelClasses = [
__DIR__ . '/src/Testing/Laravel/TestingApplication.php',
__DIR__ . '/src/Testing/Laravel/TestingContainer.php',
__DIR__ . '/src/Testing/Laravel/Filesystem/Filesystem.php',
];
$config->skip([
UnSpreadOperatorRector::class => $laravelClasses,
ReturnTypeFromStrictScalarReturnExprRector::class => $laravelClasses,
VarConstantCommentRector::class,
NewlineAfterStatementRector::class,
return RectorConfig::configure()
->withSets([__DIR__.'/vendor/larastrict/conventions/extension-rector.php'])
->withRootFiles()
->withPaths([__DIR__ . '/src', __DIR__ . '/tests'])
->withSkip([
StaticArrowFunctionRector::class => [__DIR__ . '/src/Log/Managers/ConsoleOutputManager.php'],
// We want to leave the relative constant path usage
__DIR__ . '/tests/Feature/Testing/Commands/MakeExpectationCommand/*.php',
]);
};
2 changes: 1 addition & 1 deletion src/Cache/Commands/FlushCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function handle(CacheMeService $cacheMeService): void
{
$tagOrKey = $this->argument('tagOrKey');

if (is_string($tagOrKey) === false) {
if (! is_string($tagOrKey)) {
$this->info('Only string value is supported');
return;
}
Expand Down
29 changes: 23 additions & 6 deletions src/Config/Laravel/AppConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,48 @@ class AppConfig extends AbstractConfig implements AppConfigContract
*/
public function getVersion(): string
{
return (string) ($this->get(self::KeyVersion, null, true) ?? 'DEV.' . time());
$value = ($this->get(self::KeyVersion, null, true) ?? 'DEV.' . time());
assert(is_string($value) || is_numeric($value));
return (string) $value;
}

public function getKey(): string
{
return $this->get(self::KeyKey);
$value = $this->get(self::KeyKey);
assert(is_string($value));
return $value;
}

public function getUrl(): string
{
return $this->get(self::KeyUrl, emptyToDefault: true);
$value = $this->get(self::KeyUrl, emptyToDefault: true);
assert(is_string($value));
return $value;
}

public function getAssetUrl(): ?string
{
return $this->get(self::KeyAssetUrl, null, true);
$value = $this->get(self::KeyAssetUrl, null, true);
if ($value === null) {
return null;
}

assert(is_string($value));
return $value;
}

public function getName(): string
{
return $this->get(self::KeyName, emptyToDefault: true);
$value = $this->get(self::KeyName, emptyToDefault: true);
assert(is_string($value));
return $value;
}

public function isInDebugMode(): bool
{
return (bool) $this->get(self::KeyDebug, false, true);
$value = $this->get(self::KeyDebug, false, true);
assert(is_bool($value) || is_numeric($value) || is_string($value));
return (bool) $value;
}

public function getEnvironment(): EnvironmentType|string
Expand All @@ -63,6 +79,7 @@ public function getEnvironment(): EnvironmentType|string
if (is_int($env)) {
$env = 'production';
}
assert(is_string($env));

$envType = EnvironmentType::tryFrom($env);

Expand Down
Loading

0 comments on commit 2b49efa

Please sign in to comment.