From a46915e20d46d01a5a774d189f2120e2275c8fa9 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Thu, 28 Nov 2024 19:35:35 -0600 Subject: [PATCH] Add PHP-CS-Fixer and phpstan to dev pipeline --- .gitignore | 4 +- .php-cs-fixer.php | 55 +++++++++++++++++++ .php_cs.dist | 14 ----- composer.json | 8 ++- phpstan.dist.neon | 11 ++++ src/ServiceProvider.php | 3 +- src/Services/SlugService.php | 8 +-- src/SluggableObserver.php | 2 + tests/Models/Author.php | 12 ++-- tests/Models/Post.php | 24 +++----- tests/Models/PostNotSluggable.php | 19 ++++--- tests/Models/PostWithRelation.php | 2 +- .../Models/PostWithUniqueSlugConstraints.php | 3 + tests/OnUpdateTests.php | 2 +- 14 files changed, 110 insertions(+), 57 deletions(-) create mode 100644 .php-cs-fixer.php delete mode 100644 .php_cs.dist create mode 100644 phpstan.dist.neon diff --git a/.gitignore b/.gitignore index cfca156..e172a2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ -/.php_cs.cache +/.php-cs-fixer.cache /.phpunit.cache -/.phpunit.result.cache /.idea/ /build/ /vendor/ composer.lock -composer.phar diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..befd181 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,55 @@ +in(__DIR__); + +return (new Config()) + ->setParallelConfig(ParallelConfigFactory::detect()) + ->setRules([ + '@PhpCsFixer' => true, + '@PHP84Migration' => true, + 'indentation_type' => true, + + // Overrides for (opinionated) @PhpCsFixer and @Symfony rules: + + // Align "=>" in multi-line array definitions, unless a blank line exists between elements + 'binary_operator_spaces' => ['operators' => ['=>' => 'align_single_space_minimal']], + + // Subset of statements that should be proceeded with blank line + 'blank_line_before_statement' => ['statements' => ['case', 'continue', 'default', 'return', 'throw', 'try', 'yield', 'yield_from']], + + // Enforce space around concatenation operator + 'concat_space' => ['spacing' => 'one'], + + // Use {} for empty loop bodies + 'empty_loop_body' => ['style' => 'braces'], + + // Don't change any increment/decrement styles + 'increment_style' => false, + + // Forbid multi-line whitespace before the closing semicolon + 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'], + + // Clean up PHPDocs, but leave @inheritDoc entries alone + 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'remove_inheritdoc' => false], + + // Ensure that traits are listed first in classes + // (it would be nice to enforce more, but we'll start simple) + 'ordered_class_elements' => ['order' => ['use_trait']], + + // Ensure that param and return types are sorted consistently, with null at end + 'phpdoc_types_order' => ['sort_algorithm' => 'alpha', 'null_adjustment' => 'always_last'], + + // Don't add @coversNothing annotations to tests + 'php_unit_test_class_requires_covers' => false, + + // Yoda style is too weird + 'yoda_style' => false, + ]) + ->setIndent(' ') + ->setLineEnding("\n") + ->setFinder($finder); diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index 6bc912d..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,14 +0,0 @@ -exclude('vendor') - ->in(__DIR__); - -return PhpCsFixer\Config::create() - ->setRules([ - '@PSR2' => true, - '@PHP80Migration' => true, - 'strict_param' => true, - 'array_syntax' => ['syntax' => 'short'], - ]) - ->setFinder($finder); diff --git a/composer.json b/composer.json index ca5e0f0..d0bb575 100644 --- a/composer.json +++ b/composer.json @@ -25,9 +25,12 @@ "illuminate/support": "^11.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.65", + "larastan/larastan": "^3.0", "mockery/mockery": "^1.4.4", "orchestra/testbench": "^9.0", - "pestphp/pest": "^2.28" + "pestphp/pest": "^2.28", + "phpstan/phpstan": "^2.0" }, "autoload": { "psr-4": { @@ -40,10 +43,13 @@ } }, "scripts": { + "analyze": "vendor/bin/phpstan analyze", "fresh": [ "rm -rf vendor composer.lock", "composer install" ], + "style:check": "vendor/bin/php-cs-fixer check -v", + "style:fix": "vendor/bin/php-cs-fixer fix -v", "tests": [ "rm -rf build", "XDEBUG_MODE=coverage php vendor/bin/pest" diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..8476816 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,11 @@ +includes: + - vendor/larastan/larastan/extension.neon + +parameters: + level: 4 + paths: + - resources + - src + - tests + editorUrl: '%%relFile%%:%%line%%' + editorUrlTitle: '%%relFile%%:%%line%%' diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 487abeb..7cadfb5 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -37,8 +37,9 @@ protected function setUpConfig(): void if ($this->app instanceof LaravelApplication) { $this->publishes([$source => config_path('sluggable.php')], 'config'); + /** @phpstan-ignore-next-line */ } elseif ($this->app instanceof LumenApplication) { - $this->app->configure('sluggable'); + $this->app->configure('sluggable'); /** @phpstan-ignore class.notFound */ } $this->mergeConfigFrom($source, 'sluggable'); diff --git a/src/Services/SlugService.php b/src/Services/SlugService.php index 534e099..039d6c5 100644 --- a/src/Services/SlugService.php +++ b/src/Services/SlugService.php @@ -13,6 +13,8 @@ class SlugService { + final public function __construct() {} + /** * @var \Illuminate\Database\Eloquent\Model * @var \Cviebrock\EloquentSluggable\Sluggable @@ -407,12 +409,12 @@ protected function usesSoftDeleting(): bool * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ - public static function createSlug($model, string $attribute, string $fromString, ?array $config = null): string + public static function createSlug(Model|string $model, string $attribute, string $fromString, ?array $config = null): string { if (is_string($model)) { $model = new $model; } - /** @var static $instance */ + $instance = (new static())->setModel($model); if ($config === null) { @@ -421,8 +423,6 @@ public static function createSlug($model, string $attribute, string $fromString, $modelClass = get_class($model); throw new \InvalidArgumentException("Argument 2 passed to SlugService::createSlug ['{$attribute}'] is not a valid slug attribute for model {$modelClass}."); } - } elseif (!is_array($config)) { - throw new \UnexpectedValueException('SlugService::createSlug expects an array or null as the fourth argument; ' . gettype($config) . ' given.'); } $config = $instance->getConfiguration($config); diff --git a/src/SluggableObserver.php b/src/SluggableObserver.php index f5739e3..8686a0c 100644 --- a/src/SluggableObserver.php +++ b/src/SluggableObserver.php @@ -46,6 +46,7 @@ public function __construct(SlugService $slugService, Dispatcher $events) */ public function saving(Model $model) { + /** @phpstan-ignore-next-line */ if ($model->sluggableEvent() !== self::SAVING) { return; } @@ -59,6 +60,7 @@ public function saving(Model $model) */ public function saved(Model $model) { + /** @phpstan-ignore-next-line */ if ($model->sluggableEvent() !== self::SAVED) { return; } diff --git a/tests/Models/Author.php b/tests/Models/Author.php index 566a706..2b913f7 100644 --- a/tests/Models/Author.php +++ b/tests/Models/Author.php @@ -9,23 +9,19 @@ * * @package Cviebrock\EloquentSluggable\Tests\Models * - * @property integer id - * @property string name + * @property integer $id + * @property string $name */ class Author extends Model { /** - * Indicates if the model should be timestamped. - * - * @var bool + * @inheritdoc */ public $timestamps = false; /** - * The attributes that are mass assignable. - * - * @var array + * @inheritdoc */ protected $fillable = ['name']; } diff --git a/tests/Models/Post.php b/tests/Models/Post.php index 7b46f72..2ffdb09 100644 --- a/tests/Models/Post.php +++ b/tests/Models/Post.php @@ -8,12 +8,12 @@ * * @package Cviebrock\EloquentSluggable\Tests\Models * - * @property integer id - * @property string title - * @property string|null subtitle - * @property string|null slug - * @property string|null dummy - * @property integer author_id + * @property int $id + * @property string $title + * @property ?string $subtitle + * @property ?string $slug + * @property ?string $dummy + * @property ?int $author_id */ class Post extends Model { @@ -21,23 +21,17 @@ class Post extends Model use Sluggable; /** - * The table associated with the model. - * - * @var string + * @inheritdoc */ protected $table = 'posts'; /** - * Indicates if the model should be timestamped. - * - * @var bool + * @inheritdoc */ public $timestamps = false; /** - * The attributes that are mass assignable. - * - * @var array + * @inheritdoc */ protected $fillable = ['title', 'subtitle', 'slug', 'dummy', 'author_id']; diff --git a/tests/Models/PostNotSluggable.php b/tests/Models/PostNotSluggable.php index 91a7dcb..9bcff5d 100644 --- a/tests/Models/PostNotSluggable.php +++ b/tests/Models/PostNotSluggable.php @@ -8,28 +8,29 @@ * A test model that doesn't use the Sluggable package. * * @package Cviebrock\EloquentSluggable\Tests\Models + * + * @property int $id + * @property string $title + * @property ?string $subtitle + * @property ?string $slug + * @property ?string $dummy + * @property ?int $author_id */ class PostNotSluggable extends Model { /** - * The table associated with the model. - * - * @var string + * @inheritdoc */ protected $table = 'posts'; /** - * Indicates if the model should be timestamped. - * - * @var bool + * @inheritdoc */ public $timestamps = false; /** - * The attributes that are mass assignable. - * - * @var array + * @inheritdoc */ protected $fillable = ['title', 'subtitle']; diff --git a/tests/Models/PostWithRelation.php b/tests/Models/PostWithRelation.php index ff5ea01..e1c4b6e 100644 --- a/tests/Models/PostWithRelation.php +++ b/tests/Models/PostWithRelation.php @@ -9,7 +9,7 @@ * * @package Cviebrock\EloquentSluggable\Tests\Models * - * @property \Cviebrock\EloquentSluggable\Tests\Models\Author author + * @property \Cviebrock\EloquentSluggable\Tests\Models\Author|null $author */ class PostWithRelation extends Post { diff --git a/tests/Models/PostWithUniqueSlugConstraints.php b/tests/Models/PostWithUniqueSlugConstraints.php index 4902842..d43fbcd 100644 --- a/tests/Models/PostWithUniqueSlugConstraints.php +++ b/tests/Models/PostWithUniqueSlugConstraints.php @@ -8,6 +8,8 @@ * Class PostWithUniqueSlugConstraints * * @package Cviebrock\EloquentSluggable\Tests\Models + * + * @property \Cviebrock\EloquentSluggable\Tests\Models\Author|null $author */ class PostWithUniqueSlugConstraints extends Post { @@ -27,6 +29,7 @@ public function author(): BelongsTo */ public function scopeWithUniqueSlugConstraints(Builder $query, Model $model, $attribute, $config, $slug): Builder { + /** @var self $model */ $author = $model->author; return $query->where('author_id', $author->getKey()); diff --git a/tests/OnUpdateTests.php b/tests/OnUpdateTests.php index 322e9e3..33c4aa3 100644 --- a/tests/OnUpdateTests.php +++ b/tests/OnUpdateTests.php @@ -116,7 +116,7 @@ public function testSlugDoesNotChangeIfSourceNotProvidedInModel(): void ]); self::assertEquals('my-first-post', $post->slug); - $post = Post::whereKey($post->id)->get(['id','subtitle'])->first(); + $post = Post::whereKey($post->id)->first(['id','subtitle']); $post->update([ 'subtitle' => 'A Subtitle' ]);