diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a5a00bd..116f8a8 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -5,6 +5,9 @@ Please be sure you include all the relevant information in your issue so we can - [ ] a copy of your Models that use or extend this package (just the relevant parts!) - [ ] any other code we might need to help +Please use a descriptive title for your issue. "Why doesn't this work?" doesn't provide +other users much information if they are scanning the list of issues. + Also, *please* use [fenced code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/) when pasting more than one line of code. It makes it so much more readable for everyone! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 12f2e2b..5ee8771 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,8 +4,10 @@ Please make sure you've read [CONTRIBUTING.md](https://github.com/cviebrock/eloq before submitting your pull request, and that you have: - [ ] provided a rationale for your change (I try not to add features that are going to have a limited user-base) -- [ ] used the [PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +- [ ] checked your coding style with `composer run style:check` +- [ ] analyzed your code with `composer run analyze` - [ ] added tests +- [ ] confirm all old and new tests pass with `composer run tests` - [ ] documented any change in behaviour (e.g. updated the `README.md`, etc.) - [ ] only submitted one pull request per feature diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 69d1f1b..5606692 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,54 +3,60 @@ name: tests on: [push, pull_request] jobs: - tests: - runs-on: ubuntu-latest + tests: + runs-on: ubuntu-latest - services: - mysql: - image: mysql:8.0 - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: testing - ports: - - 3306 - options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: testing + ports: + - 3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 - strategy: - fail-fast: false - matrix: - php: [8.2, 8.3] - stability: [prefer-lowest, prefer-stable] + strategy: + fail-fast: false + matrix: + php: + - version: 8.2 + - version: 8.3 + - version: 8.4 + env: PHP_CS_FIXER_IGNORE_ENV=1 + stability: [prefer-lowest, prefer-stable] - name: PHP ${{ matrix.php }} - ${{ matrix.stability }} + name: PHP ${{ matrix.php.version }} - ${{ matrix.stability }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - tools: composer:v2, cs2pr - coverage: none - - name: Setup problem matchers for PHPUnit - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: dependencies-${{ matrix.php }}-${{ matrix.stability }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: dependencies-${{ matrix.php }}-${{ matrix.stability }}-composer- - - name: Install dependencies - run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - # - name: Check coding style - # run: php-cs-fixer fix --dry-run --allow-risky=true --format=checkstyle | cs2pr - - name: Configure matchers for PHPUnit - uses: mheap/phpunit-matcher-action@v1 - - name: Run PHP tests via PHPUnit - run: vendor/bin/pest --teamcity - env: - DB_PORT: ${{ job.services.mysql.ports[3306] }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php.version }} + tools: composer:v2, cs2pr + coverage: none + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: dependencies-${{ matrix.php.version }}-${{ matrix.stability }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: dependencies-${{ matrix.php.version }}-${{ matrix.stability }}-composer- + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Check coding style + run: ${{ matrix.php.env }} composer run style:check -- --format=checkstyle | cs2pr + - name: Check static analysis + run: composer run analyze -- --error-format=checkstyle | cs2pr + - name: Configure matchers for PHPUnit + uses: mheap/phpunit-matcher-action@v1 + - name: Run PHP tests + run: composer run tests:ci + env: + DB_PORT: ${{ job.services.mysql.ports[3306] }} diff --git a/.gitignore b/.gitignore index 33c7c64..38275f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ -/.php_cs.cache -/.phpunit.result.cache +/.idea +/.php-cs-fixer.cache +/.phpunit.cache /.vscode /build/ /vendor/ composer.lock -composer.phar -.phpunit.cache \ No newline at end of file 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/.semver b/.semver index 5e666da..6adae90 100644 --- a/.semver +++ b/.semver @@ -1,6 +1,6 @@ --- :major: 11 :minor: 0 -:patch: 1 +:patch: 2 :special: '' :metadata: '' diff --git a/CHANGELOG.md b/CHANGELOG.md index cd831dc..cc0e346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 11.0.2 - 20-Dec-2024 + +- Support PHP 8.4 (#148, thanks @tectiv3) +- various other code style and analysis fixes + + ## 11.0.1 - 06-Jun-2024 - make return of `allTags()` consistent (#146, thanks @y1n0) diff --git a/composer.json b/composer.json index 8faa509..926bc2d 100644 --- a/composer.json +++ b/composer.json @@ -27,8 +27,11 @@ "illuminate/support": "^11.0" }, "require-dev": { + "friendsofphp/php-cs-fixer": "^3.65", + "larastan/larastan": "^3.0", "orchestra/testbench": "^9.0", - "pestphp/pest": "^2.28" + "pestphp/pest": "^2.28", + "phpstan/phpstan": "^2.0" }, "autoload": { "psr-4": { @@ -41,14 +44,20 @@ } }, "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" ], + "tests:ci": [ + "vendor/bin/pest --teamcity" + ], "post-autoload-dump": [ "@php ./vendor/bin/testbench package:discover --ansi" ] diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..a2aeacd --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,11 @@ +includes: + - vendor/larastan/larastan/extension.neon + +parameters: + level: 1 + paths: + - resources + - src + - tests + editorUrl: '%%relFile%%:%%line%%' + editorUrlTitle: '%%relFile%%:%%line%%' diff --git a/src/Events/ModelTagged.php b/src/Events/ModelTagged.php index ec54a68..225cfd5 100644 --- a/src/Events/ModelTagged.php +++ b/src/Events/ModelTagged.php @@ -43,14 +43,4 @@ public function __construct($model, $tags) $this->model = $model; $this->tags = $tags; } - - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - - } } diff --git a/src/Events/ModelUntagged.php b/src/Events/ModelUntagged.php index 0e0d6a8..1782b3c 100644 --- a/src/Events/ModelUntagged.php +++ b/src/Events/ModelUntagged.php @@ -44,13 +44,4 @@ public function __construct($model, $tags) $this->tags = $tags; } - /** - * Get the channels the event should broadcast on. - * - * @return \Illuminate\Broadcasting\Channel|array - */ - public function broadcastOn() - { - - } } diff --git a/src/Models/Tag.php b/src/Models/Tag.php index 53228c2..92d6788 100644 --- a/src/Models/Tag.php +++ b/src/Models/Tag.php @@ -9,6 +9,10 @@ /** * Class Tag + * + * @property int $tag_id + * @property string $name + * @property string $normalized */ class Tag extends Model { @@ -93,6 +97,8 @@ public function isRelation($key) $this->setRelation($key, $results); }); } + + return false; } /** diff --git a/src/Services/TagService.php b/src/Services/TagService.php index a1819f8..f5f9062 100644 --- a/src/Services/TagService.php +++ b/src/Services/TagService.php @@ -199,7 +199,7 @@ public function normalize(string $string): string * * @param \Illuminate\Database\Eloquent\Model|string|null $class * - * @return Collection + * @return Collection */ public function getAllTags($class = null): Collection { @@ -253,7 +253,7 @@ public function getAllTagsArrayNormalized($class = null): array /** * Get all Tags that are unused by any model. * - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Database\Eloquent\Collection */ public function getAllUnusedTags(): Collection { @@ -275,9 +275,9 @@ public function getAllUnusedTags(): Collection * @param \Illuminate\Database\Eloquent\Model|string|null $class * @param int $minCount * - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Database\Eloquent\Collection */ - public function getPopularTags(int $limit = null, $class = null, int $minCount = 1): Collection + public function getPopularTags(?int $limit = null, Model|string|null $class = null, int $minCount = 1): Collection { $tagTable = $this->getQualifiedTagTableName(); $pivotTable = $this->getQualifiedPivotTableName(); @@ -319,7 +319,7 @@ public function getPopularTags(int $limit = null, $class = null, int $minCount = * * @return int */ - public function renameTags(string $oldName, string $newName, $class = null): int + public function renameTags(string $oldName, string $newName, Model|string|null $class = null): int { // If no class is specified, we can do the rename with a simple SQL update if ($class === null) { @@ -377,11 +377,11 @@ private function getQualifiedTagTableName(): string /** * Get the qualified table name for the Tag model's pivot table. * - * @param string $class + * @param ?string $class * * @return string */ - private function getQualifiedPivotTableName(string $class=null): string + private function getQualifiedPivotTableName(?string $class=null): string { /** @var \Cviebrock\EloquentTaggable\Taggable $instance */ $instance = $class diff --git a/tests/Configuration/ScopeExceptionTests.php b/tests/Configuration/ScopeExceptionTests.php index 8170a96..a9220c5 100644 --- a/tests/Configuration/ScopeExceptionTests.php +++ b/tests/Configuration/ScopeExceptionTests.php @@ -29,7 +29,6 @@ public function testWithAllTagsEmpty() { $this->expectException(NoTagsSpecifiedException::class); - /** @var Collection $models */ TestModel::withAllTags('')->get(); } @@ -40,7 +39,6 @@ public function testWithAnyTagsEmpty() { $this->expectException(NoTagsSpecifiedException::class); - /** @var Collection $models */ TestModel::withAnyTags('')->get(); } } diff --git a/tests/ConnectionTests.php b/tests/ConnectionTests.php index 6311d0b..b9a6278 100644 --- a/tests/ConnectionTests.php +++ b/tests/ConnectionTests.php @@ -108,6 +108,7 @@ public function testPrefixWhenGettingAllTags(): void $popularTags = $this->service->getPopularTags(1); self::assertCount(1, $popularTags); + /** @var Tag $popularTag */ $popularTag = $popularTags->first(); self::assertEquals('Banana', $popularTag->name); self::assertEquals(2, $popularTag->taggable_count); @@ -117,6 +118,7 @@ public function testPrefixWhenGettingAllTags(): void $unusedTags = $this->service->getAllUnusedTags(); self::assertCount(1, $unusedTags); + /** @var Tag $unusedTag */ $unusedTag = $unusedTags->first(); self::assertEquals('Durian',$unusedTag->name); diff --git a/tests/TestCase.php b/tests/TestCase.php index fb51e3c..20d2454 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -21,6 +21,7 @@ public function setUp(): void $this->beforeApplicationDestroyed(static function() { (new \CreateTestModelsTable())->down(); + /** @phpstan-ignore-next-line class.notFound */ (new \CreateTaggableTable())->down(); }); } @@ -84,6 +85,7 @@ protected function newDummy(array $data = ['title' => 'dummy']): TestDummy private function setUpDatabase(): void { include_once __DIR__.'/../resources/database/migrations/create_taggable_table.php.stub'; + /** @phpstan-ignore-next-line class.notFound */ (new \CreateTaggableTable())->up(); include_once __DIR__.'/database/migrations/2013_11_04_163552_create_test_models_table.php'; diff --git a/tests/TestDummy.php b/tests/TestDummy.php index 5919afe..d778aea 100644 --- a/tests/TestDummy.php +++ b/tests/TestDummy.php @@ -6,6 +6,9 @@ /** * Class TestDummy + * + * @property int $id + * @property string $title */ class TestDummy extends Model { diff --git a/tests/TestModel.php b/tests/TestModel.php index c7a7d0b..25ecf12 100644 --- a/tests/TestModel.php +++ b/tests/TestModel.php @@ -7,6 +7,9 @@ /** * Class TestModel + * + * @property int $id + * @property string $title */ class TestModel extends Model {