From 9225f514d1e8b4a1193de8f8417e8534f7420f85 Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Sat, 29 Jun 2019 09:18:50 -0700 Subject: [PATCH 01/32] Add @matt-allan's change to changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b7b1171..a4112bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Next + +### Enhancements +- Add links for all available includes to `JsonApiSerializer` #331 - Thanks @matt-allan + + + ## 0.18.0 - 2019-05-09 ### Enhancements From 7fa113fef4035feb3879d2ad05b1dc48bdbbaed2 Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Sat, 29 Jun 2019 13:07:55 -0700 Subject: [PATCH 02/32] Add phpcs and circleci configurations --- .circleci/config.yml | 42 +++++++++++++++++++ .gitignore | 1 + composer.json | 2 +- phpcs.xml.dist | 14 +++++++ src/Manager.php | 2 +- .../PhalconFrameworkPaginatorAdapter.php | 2 +- src/Serializer/JsonApiSerializer.php | 8 ++-- src/Serializer/Serializer.php | 2 +- 8 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 .circleci/config.yml create mode 100644 phpcs.xml.dist diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..b6e3aaf8 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,42 @@ +# PHP CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-php/ for more details +# +version: 2 +jobs: + build: + docker: + # Specify the version you desire here + - image: circleci/php:7.2 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # Using the RAM variation mitigates I/O contention + # for database intensive operations. + # - image: circleci/mysql:5.7-ram + # + # - image: redis:2.8.19 + + steps: + - checkout + + - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev + + # Download and cache dependencies + - restore_cache: + keys: + # "composer.lock" can be used if it is committed to the repo + - v1-dependencies-{{ checksum "composer.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: composer install -n --prefer-dist + + - save_cache: + key: v1-dependencies-{{ checksum "composer.json" }} + paths: + - ./vendor + + # Check code style + - run: ./vendor/bin/phpcs src diff --git a/.gitignore b/.gitignore index a1ecf70f..aaee0d9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea .ruby-version composer.lock build diff --git a/composer.json b/composer.json index 843c20c6..31736618 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "mockery/mockery": "~0.9", "pagerfanta/pagerfanta": "~1.0.0", "phpunit/phpunit": "^4.8.35 || ^7.5", - "squizlabs/php_codesniffer": "~1.5", + "squizlabs/php_codesniffer": "~1.5|~2.0|~3.4", "zendframework/zend-paginator": "~2.3" }, "suggest": { diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..dfb69119 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,14 @@ + + + Fractal Coding Standards + + + + + + + + + + + diff --git a/src/Manager.php b/src/Manager.php index 23a93ce9..2d565aaf 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -223,7 +223,7 @@ public function parseIncludes($includes) /** * Parse field parameter. * - * @param array $fieldsets Array of fields to include. It must be an array whose keys + * @param array $fieldsets Array of fields to include. It must be an array whose keys * are resource types and values an array or a string * of the fields to return, separated by a comma * diff --git a/src/Pagination/PhalconFrameworkPaginatorAdapter.php b/src/Pagination/PhalconFrameworkPaginatorAdapter.php index d2bec715..e6f666dd 100644 --- a/src/Pagination/PhalconFrameworkPaginatorAdapter.php +++ b/src/Pagination/PhalconFrameworkPaginatorAdapter.php @@ -93,7 +93,7 @@ public function getPerPage() * Get the next. * * @return int - */ + */ public function getNext() { return $this->paginator->next; diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index 3b2efb53..85d54e5f 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -72,17 +72,17 @@ public function item($resourceKey, array $data) unset($resource['data']['attributes']['id']); - if(isset($resource['data']['attributes']['links'])) { + if (isset($resource['data']['attributes']['links'])) { $custom_links = $data['links']; unset($resource['data']['attributes']['links']); } - if (isset($resource['data']['attributes']['meta'])){ + if (isset($resource['data']['attributes']['meta'])) { $resource['data']['meta'] = $data['meta']; unset($resource['data']['attributes']['meta']); } - if(empty($resource['data']['attributes'])) { + if (empty($resource['data']['attributes'])) { $resource['data']['attributes'] = (object) []; } @@ -90,7 +90,7 @@ public function item($resourceKey, array $data) $resource['data']['links'] = [ 'self' => "{$this->baseUrl}/$resourceKey/$id", ]; - if(isset($custom_links)) { + if (isset($custom_links)) { $resource['data']['links'] = array_merge($resource['data']['links'], $custom_links); } } diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php index 06aac300..e3252b08 100644 --- a/src/Serializer/Serializer.php +++ b/src/Serializer/Serializer.php @@ -99,4 +99,4 @@ public function injectData($data, $rawIncludedData); * @return array */ public function filterIncludes($includedData, $data); -} \ No newline at end of file +} From 1cb9cd79fb60bba6c8cf85d8bde49659c9f5b627 Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Thu, 4 Jul 2019 14:10:02 -0700 Subject: [PATCH 03/32] Add maintainers to the readme (#488) I did this in alphabetical order :D --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 1efbc2ee..f1a0734b 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,12 @@ $ phpunit Please see [CONTRIBUTING](https://github.com/thephpleague/fractal/blob/master/CONTRIBUTING.md) and [CONDUCT](https://github.com/thephpleague/fractal/blob/master/CONDUCT.md) for details. + +## Maintainers + +- [Korvin Szanto](https://github.com/korvinszanto) +- [Matt Trask](https://github.com/matthewtrask) + ## Credits - [Graham Daniels](https://github.com/greydnls) From ecf884e2136354468b805f311edc2e39ae3bfb5b Mon Sep 17 00:00:00 2001 From: Christian Thomas Date: Thu, 4 Jul 2019 17:11:17 -0400 Subject: [PATCH 04/32] Implement \JsonSerializable for Scopes (#462) * Implement \JsonSerializable for Scopes * Update CHANGELOG.md noting changes from #462 --- CHANGELOG.md | 1 + src/Scope.php | 12 ++++++++++-- test/ScopeTest.php | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4112bbe..83b2e97f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Enhancements - Add links for all available includes to `JsonApiSerializer` #331 - Thanks @matt-allan +- Implement interface `\JsonSerializable` in Scopes to allow for direct usage with `json_encode()` diff --git a/src/Scope.php b/src/Scope.php index 6f69b579..2704a063 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -26,7 +26,7 @@ * context. For example, the same resource could be attached to multiple scopes. * There are root scopes, parent scopes and child scopes. */ -class Scope +class Scope implements \JsonSerializable { /** * @var array @@ -293,6 +293,14 @@ public function toArray() } /** + * @return array + */ + public function jsonSerialize() + { + return $this->toArray(); + } + + /** * Convert the current data for this scope to JSON. * * @param int $options @@ -301,7 +309,7 @@ public function toArray() */ public function toJson($options = 0) { - return json_encode($this->toArray(), $options); + return json_encode($this, $options); } /** diff --git a/test/ScopeTest.php b/test/ScopeTest.php index 6ee8236d..3a3cd227 100755 --- a/test/ScopeTest.php +++ b/test/ScopeTest.php @@ -72,6 +72,23 @@ public function testToArray() $this->assertSame(['data' => ['foo' => 'bar']], $scope->toArray()); } + /** + * @covers \League\Fractal\Scope::jsonSerialize() + */ + public function testJsonSerializable() + { + $manager = new Manager(); + + $resource = new Item(['foo' => 'bar'], function ($data) { + return $data; + }); + + $scope = new Scope($manager, $resource); + + $this->assertInstanceOf('\JsonSerializable', $scope); + $this->assertEquals($scope->jsonSerialize(), $scope->toArray()); + } + public function testToJson() { $data = [ From e03925f249c40b3b90b09c55aae4c8200e680dba Mon Sep 17 00:00:00 2001 From: Mariano Darc Date: Thu, 4 Jul 2019 23:46:09 +0200 Subject: [PATCH 05/32] Fix phpDoc param typehint for setters in Cursor class (#463) Change typehints in the phpDoc for the setters to match CursorInterface types. --- src/Pagination/Cursor.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Pagination/Cursor.php b/src/Pagination/Cursor.php index f267e538..90ca1614 100644 --- a/src/Pagination/Cursor.php +++ b/src/Pagination/Cursor.php @@ -78,7 +78,7 @@ public function getCurrent() /** * Set the current cursor value. * - * @param int $current + * @param mixed $current * * @return Cursor */ @@ -102,7 +102,7 @@ public function getPrev() /** * Set the prev cursor value. * - * @param int $prev + * @param mixed $prev * * @return Cursor */ @@ -126,7 +126,7 @@ public function getNext() /** * Set the next cursor value. * - * @param int $next + * @param mixed $next * * @return Cursor */ From b323c88066cccf57ffc16019c89292f290170f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Onur=20=C5=9Eim=C5=9Fek?= Date: Thu, 1 Aug 2019 23:29:16 +0300 Subject: [PATCH 06/32] Allow sub relations with modifier --- src/Manager.php | 6 ++++++ test/ManagerTest.php | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/Manager.php b/src/Manager.php index 2d565aaf..b3eb9491 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -164,6 +164,7 @@ public function parseIncludes($includes) { // Wipe these before we go again $this->requestedIncludes = $this->includeParams = []; + $subRelations = ''; if (is_string($includes)) { $includes = explode(',', $includes); @@ -177,6 +178,7 @@ public function parseIncludes($includes) foreach ($includes as $include) { list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null); + list($allModifiersStr, $subRelations) = array_pad(explode('.', $allModifiersStr, 2), 2, null); // Trim it down to a cool level of recursion $includeName = $this->trimToAcceptableRecursionLevel($includeName); @@ -212,6 +214,10 @@ public function parseIncludes($includes) } $this->includeParams[$includeName] = $modifierArr; + + if ($subRelations) { + $this->requestedIncludes[] = $this->trimToAcceptableRecursionLevel($includeName . '.' . $subRelations); + } } // This should be optional and public someday, but without it includes would never show up diff --git a/test/ManagerTest.php b/test/ManagerTest.php index fbfbd174..475c52af 100755 --- a/test/ManagerTest.php +++ b/test/ManagerTest.php @@ -56,6 +56,10 @@ public function testParseIncludes() $manager->parseIncludes(['foo', 'foo', 'bar']); $this->assertSame(['foo', 'bar'], $manager->getRequestedIncludes()); + $manager->parseIncludes(['foo.bar', 'foo:limit(10|1).bar']); + $this->assertSame(['foo', 'foo.bar'], $manager->getRequestedIncludes()); + $this->assertSame(['10', '1'], $manager->getIncludeParams('foo')->get('limit')); + // Do requests for `baz.bart` also request `baz`? $manager->parseIncludes(['foo.bar']); $this->assertSame(['foo', 'foo.bar'], $manager->getRequestedIncludes()); @@ -74,6 +78,17 @@ public function testParseIncludes() $this->assertSame([''], $params['anotherparam']); $this->assertNull($params['totallymadeup']); + + // Relation with params and sub relation + $manager->parseIncludes('foo:limit(5|1):order(name).bar,baz'); + + $params = $manager->getIncludeParams('foo'); + + $this->assertInstanceOf('League\Fractal\ParamBag', $params); + + $this->assertSame(['5', '1'], $params['limit']); + $this->assertSame(['name'], $params['order']); + $this->assertSame(['foo', 'foo.bar', 'baz'], $manager->getRequestedIncludes()); } public function testParseExcludeSelfie() @@ -151,6 +166,24 @@ public function testRecursionLimiting() $manager->getRequestedIncludes() ); + $manager->parseIncludes('a:limit(5|1).b.c.d.e.f.g.h.i.j.NEVER'); + + $this->assertSame( + [ + 'a', + 'a.b', + 'a.b.c', + 'a.b.c.d', + 'a.b.c.d.e', + 'a.b.c.d.e.f', + 'a.b.c.d.e.f.g', + 'a.b.c.d.e.f.g.h', + 'a.b.c.d.e.f.g.h.i', + 'a.b.c.d.e.f.g.h.i.j', + ], + $manager->getRequestedIncludes() + ); + // Try setting to 3 and see what happens $manager->setRecursionLimit(3); $manager->parseIncludes('a.b.c.NEVER'); @@ -163,6 +196,17 @@ public function testRecursionLimiting() ], $manager->getRequestedIncludes() ); + + $manager->parseIncludes('a:limit(5|1).b.c.NEVER'); + + $this->assertSame( + [ + 'a', + 'a.b', + 'a.b.c', + ], + $manager->getRequestedIncludes() + ); } public function testCreateDataWithCallback() From c99aa52a6e4d3e1d12f219e0dc8078be0be40d00 Mon Sep 17 00:00:00 2001 From: matt trask Date: Fri, 24 Jan 2020 07:55:25 -0600 Subject: [PATCH 07/32] update travis to allow failures on 54 and 55 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index f8d1e770..c1ebb9e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,9 @@ matrix: allow_failures: - php: hhvm + - php: 5.4 + - php: 5.5 + install: - travis_retry composer install --no-interaction --prefer-source From 276f1f318534703efd3252b95b80d710b1417d20 Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Fri, 24 Jan 2020 14:46:14 -0800 Subject: [PATCH 08/32] Fix tests for PHP 5.4 and 5.5 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c1ebb9e1..940d96b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,9 @@ sudo: false matrix: include: - php: 5.4 + dist: trusty - php: 5.5 + dist: trusty - php: 5.6 env: COLLECT_COVERAGE=true - php: 7.0 @@ -17,8 +19,6 @@ matrix: allow_failures: - php: hhvm - - php: 5.4 - - php: 5.5 install: From c1042fd31fd33d86fea7a3a54f643ad08905dc96 Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Sat, 22 Aug 2020 11:12:21 -0700 Subject: [PATCH 09/32] Update branch alias to reflect current status (#516) --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 31736618..ef21a865 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ }, "extra": { "branch-alias": { - "dev-master": "0.13-dev" + "dev-master": "0.20.x-dev" } } } From 9c9c493edcf0f5438555e5b515a790749a26c807 Mon Sep 17 00:00:00 2001 From: Steve McDougall Date: Fri, 25 Sep 2020 17:41:34 +0100 Subject: [PATCH 10/32] Move to Github Actions (#517) * Adding test scripts to composer.json for ease of use * Testing github actions based off #501 * Fixing typo in matric * Fixing setup php configuration in github action * Sending coverage off to scrutinizer * Removing circle ci config * Matching travis PHP version checks * Commenting out coverage report for scrutinizer * Removing scrutinizer sending for now * Testing github action and removing travis * Adding travis config back - as it is causing issues on the PR * Pushing to separate workflows * Renaming main to run tests * Adding scrutinizer steps as final steps so coverage report can be sent * Swapping travis badge for github actions badge Co-authored-by: Steve McDougall --- .circleci/config.yml | 42 ------------------------- .github/workflows/run-tests.yml | 49 +++++++++++++++++++++++++++++ .github/workflows/style-checker.yml | 32 +++++++++++++++++++ .travis.yml | 2 +- README.md | 2 +- composer.json | 5 +++ 6 files changed, 88 insertions(+), 44 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .github/workflows/run-tests.yml create mode 100644 .github/workflows/style-checker.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b6e3aaf8..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,42 +0,0 @@ -# PHP CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-php/ for more details -# -version: 2 -jobs: - build: - docker: - # Specify the version you desire here - - image: circleci/php:7.2 - - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # Using the RAM variation mitigates I/O contention - # for database intensive operations. - # - image: circleci/mysql:5.7-ram - # - # - image: redis:2.8.19 - - steps: - - checkout - - - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev - - # Download and cache dependencies - - restore_cache: - keys: - # "composer.lock" can be used if it is committed to the repo - - v1-dependencies-{{ checksum "composer.json" }} - # fallback to using the latest cache if no exact match is found - - v1-dependencies- - - - run: composer install -n --prefer-dist - - - save_cache: - key: v1-dependencies-{{ checksum "composer.json" }} - paths: - - ./vendor - - # Check code style - - run: ./vendor/bin/phpcs src diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml new file mode 100644 index 00000000..7feb95cd --- /dev/null +++ b/.github/workflows/run-tests.yml @@ -0,0 +1,49 @@ +name: The PHP League Tests + +on: [push, pull_request] + +jobs: + ci: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest] + php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4] + + name: League - PHP ${{ matrix.php }} on ${{ matrix.os }} + + steps: + + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: phpcs, phpunit + + - name: Validate Composer + run: composer validate + + - name: Cache Dependencies + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ matrix.os }}-composer-cache-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ matrix.os }}-php-${{ matrix.php }} + + - name: Install Dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Execute Tests + run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover + + - name: Download Scrutinizer phar + run: wget https://scrutinizer-ci.com/ocular.phar + + - name: Send Coverage Report to Scrutinizer + run: php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/.github/workflows/style-checker.yml b/.github/workflows/style-checker.yml new file mode 100644 index 00000000..68b72297 --- /dev/null +++ b/.github/workflows/style-checker.yml @@ -0,0 +1,32 @@ +name: The PHP League Style Checks + +on: [push, pull_request] + +jobs: + style: + runs-on: ubuntu-latest + name: The PHP League Style Checks + steps: + + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + + - name: Cache Dependencies + id: composer-cache-style + uses: actions/cache@v2 + with: + path: vendor + key: ubuntu-composer-cache-style-${{ hashFiles('**/composer.lock') }} + restore-keys: ubuntu-php-style + + - name: Install Dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --prefer-dist --no-progress --no-suggest + + - name: Check Coding Style + run: vendor/bin/phpcs src/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 940d96b9..8c60cd6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,4 @@ script: - if [[ "$COLLECT_COVERAGE" == "true" ]]; then vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover; else vendor/bin/phpunit --no-coverage; fi after_script: - - if [[ "$COLLECT_COVERAGE" == "true" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi + - if [[ "$COLLECT_COVERAGE" == "true" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi \ No newline at end of file diff --git a/README.md b/README.md index f1a0734b..6573ee6b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Latest Version](https://img.shields.io/github/release/thephpleague/fractal.svg?style=flat-square)](https://github.com/thephpleague/fractal/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) -[![Build Status](https://img.shields.io/travis/thephpleague/fractal/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/fractal) +![The PHP League Tests](https://github.com/thephpleague/fractal/workflows/The%20PHP%20League%20Tests/badge.svg) [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/fractal.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/fractal/code-structure) [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/fractal.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/fractal) [![Total Downloads](https://img.shields.io/packagist/dt/league/fractal.svg?style=flat-square)](https://packagist.org/packages/league/fractal) diff --git a/composer.json b/composer.json index ef21a865..2d0ba2ea 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,11 @@ "League\\Fractal\\Test\\": "test" } }, + "scripts": { + "check": "vendor/bin/phpcs src/", + "test": "vendor/bin/phpunit --testdox --colors=always", + "test:coverage": "vendor/bin/phpunit --coverage-html build/coverage" + }, "extra": { "branch-alias": { "dev-master": "0.20.x-dev" From ebc071a69d142b2380ea343e563dbc151b234644 Mon Sep 17 00:00:00 2001 From: Sergiy Petrov Date: Mon, 7 Mar 2022 21:48:32 +0200 Subject: [PATCH 11/32] Test against php 8.0 (#520) --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 7feb95cd..1b05ae84 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest] - php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4] + php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] name: League - PHP ${{ matrix.php }} on ${{ matrix.os }} From 9dcd8310dec36012452b8de4866b7a0f04ef236a Mon Sep 17 00:00:00 2001 From: Matt Trask Date: Mon, 7 Mar 2022 14:53:40 -0500 Subject: [PATCH 12/32] clean up php code sniffer violations and update composer.json to 7.0 (#534) * clean up php code sniffer violations and update composer.json to 7.0 * remove the string typehint til 7.1 so we can pass the nullable flag * add return type hints to ScopeFactoryInterface Co-authored-by: = <=> --- composer.json | 2 +- src/ScopeFactory.php | 10 +++++++--- src/ScopeFactoryInterface.php | 13 +++++++++++-- src/Serializer/JsonApiSerializer.php | 15 ++++++++++++--- src/TransformerAbstract.php | 15 ++++++++++++++- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 2d0ba2ea..061efc82 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "sort-packages": true }, "require": { - "php": ">=5.4" + "php": ">=7.0" }, "require-dev": { "doctrine/orm": "^2.5", diff --git a/src/ScopeFactory.php b/src/ScopeFactory.php index 1d173650..ccb02e4e 100644 --- a/src/ScopeFactory.php +++ b/src/ScopeFactory.php @@ -21,7 +21,7 @@ class ScopeFactory implements ScopeFactoryInterface * @param string|null $scopeIdentifier * @return Scope */ - public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null) + public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null): Scope { return new Scope($manager, $resource, $scopeIdentifier); } @@ -33,8 +33,12 @@ public function createScopeFor(Manager $manager, ResourceInterface $resource, $s * @param string|null $scopeIdentifier * @return Scope */ - public function createChildScopeFor(Manager $manager, Scope $parentScopeInstance, ResourceInterface $resource, $scopeIdentifier = null) - { + public function createChildScopeFor( + Manager $manager, + Scope $parentScopeInstance, + ResourceInterface $resource, + $scopeIdentifier = null + ): Scope { $scopeInstance = $this->createScopeFor($manager, $resource, $scopeIdentifier); // This will be the new children list of parents (parents parents, plus the parent) diff --git a/src/ScopeFactoryInterface.php b/src/ScopeFactoryInterface.php index 1175712b..fe247c6d 100644 --- a/src/ScopeFactoryInterface.php +++ b/src/ScopeFactoryInterface.php @@ -26,7 +26,11 @@ interface ScopeFactoryInterface * @param string|null $scopeIdentifier * @return Scope */ - public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null); + public function createScopeFor( + Manager $manager, + ResourceInterface $resource, + $scopeIdentifier = null + ): Scope; /** * @param Manager $manager @@ -35,5 +39,10 @@ public function createScopeFor(Manager $manager, ResourceInterface $resource, $s * @param string|null $scopeIdentifier * @return Scope */ - public function createChildScopeFor(Manager $manager, Scope $parentScope, ResourceInterface $resource, $scopeIdentifier = null); + public function createChildScopeFor( + Manager $manager, + Scope $parentScope, + ResourceInterface $resource, + $scopeIdentifier = null + ): Scope; } diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index 85d54e5f..55938dab 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -188,7 +188,12 @@ public function includedData(ResourceInterface $resource, array $data) } $includeObjects = $this->createIncludeObjects($includeObject); - list($serializedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey($includeObjects, $linkedIds, $serializedData); + + list($serializedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey( + $includeObjects, + $linkedIds, + $serializedData + ); } } @@ -402,7 +407,11 @@ protected function pullOutNestedIncludedData(array $data) foreach ($data as $value) { foreach ($value as $includeObject) { if (isset($includeObject['included'])) { - list($includedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey($includeObject['included'], $linkedIds, $includedData); + list($includedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey( + $includeObject['included'], + $linkedIds, + $includedData + ); } } } @@ -598,7 +607,7 @@ private function addRelationshipLinks($resource, $relationshipKey) $resource['relationships'][$relationshipKey] = array_merge( [ 'links' => [ - 'self' => "{$this->baseUrl}/{$resource['type']}/{$resource['id']}/relationships/{$relationshipKey}", + 'self' => "{$this->baseUrl}/{$resource['type']}/{$resource['id']}/relationships/{$relationshipKey}", 'related' => "{$this->baseUrl}/{$resource['type']}/{$resource['id']}/{$relationshipKey}", ] ], diff --git a/src/TransformerAbstract.php b/src/TransformerAbstract.php index 866c51d4..59d8a8c9 100644 --- a/src/TransformerAbstract.php +++ b/src/TransformerAbstract.php @@ -182,10 +182,23 @@ private function includeResourceIfAvailable( protected function callIncludeMethod(Scope $scope, $includeName, $data) { $scopeIdentifier = $scope->getIdentifier($includeName); + $params = $scope->getManager()->getIncludeParams($scopeIdentifier); // Check if the method name actually exists - $methodName = 'include'.str_replace(' ', '', ucwords(str_replace('_', ' ', str_replace('-', ' ', $includeName)))); + $methodName = 'include'.str_replace( + ' ', + '', + ucwords(str_replace( + '_', + ' ', + str_replace( + '-', + ' ', + $includeName + ) + )) + ); $resource = call_user_func([$this, $methodName], $data, $params); From b42d735528a97c13617caad1b9016a4e9b57068d Mon Sep 17 00:00:00 2001 From: madagon Date: Mon, 7 Mar 2022 22:53:54 +0300 Subject: [PATCH 13/32] jsonSerialize fix php8 interface compatibility (#527) --- src/Scope.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scope.php b/src/Scope.php index 2704a063..1cc9d10d 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -293,9 +293,9 @@ public function toArray() } /** - * @return array + * @return mixed */ - public function jsonSerialize() + public function jsonSerialize(): mixed { return $this->toArray(); } From fc67d130c629c5b9ba31e0284bdee9c10e100bd1 Mon Sep 17 00:00:00 2001 From: Anne van de Venis Date: Mon, 7 Mar 2022 20:56:45 +0100 Subject: [PATCH 14/32] Add support for PHP 8.1 (#528) * Use #[\ReturnTypeWillChange] * Fix ArrayAccess interface * Fix(): Deprecated: explode(): Passing null to parameter #2 ($string) of type string is deprecated --- src/Manager.php | 2 +- src/ParamBag.php | 5 +++++ src/Scope.php | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Manager.php b/src/Manager.php index b3eb9491..4dd4f40c 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -177,7 +177,7 @@ public function parseIncludes($includes) } foreach ($includes as $include) { - list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null); + list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, ''); list($allModifiersStr, $subRelations) = array_pad(explode('.', $allModifiersStr, 2), 2, null); // Trim it down to a cool level of recursion diff --git a/src/ParamBag.php b/src/ParamBag.php index 0ddb7321..1810ac3d 100644 --- a/src/ParamBag.php +++ b/src/ParamBag.php @@ -105,6 +105,7 @@ public function __unset($key) * * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($key) { return $this->__isset($key); @@ -117,6 +118,7 @@ public function offsetExists($key) * * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->__get($key); @@ -132,6 +134,7 @@ public function offsetGet($key) * * @return void */ + #[\ReturnTypeWillChange] public function offsetSet($key, $value) { throw new \LogicException('Modifying parameters is not permitted'); @@ -146,6 +149,7 @@ public function offsetSet($key, $value) * * @return void */ + #[\ReturnTypeWillChange] public function offsetUnset($key) { throw new \LogicException('Modifying parameters is not permitted'); @@ -156,6 +160,7 @@ public function offsetUnset($key) * * @return \ArrayIterator */ + #[\ReturnTypeWillChange] public function getIterator() { return new \ArrayIterator($this->params); diff --git a/src/Scope.php b/src/Scope.php index 1cc9d10d..f79d9f39 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -295,7 +295,8 @@ public function toArray() /** * @return mixed */ - public function jsonSerialize(): mixed + #[\ReturnTypeWillChange] + public function jsonSerialize() { return $this->toArray(); } From aa9f1275d9b2ae9569645b13a4458cef47582b6d Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:03:48 +0100 Subject: [PATCH 15/32] Make sure CI is green (#535) * Make sure CI is green * Remove .travis.yml * Minor CS * Fixed a bunch of tests * Remove scrutinizer --- .github/workflows/run-tests.yml | 7 ++--- .gitignore | 5 +-- .scrutinizer.yml | 22 ------------- .travis.yml | 31 ------------------- README.md | 2 -- composer.json | 8 ++--- test/ManagerTest.php | 2 +- .../DoctrinePaginatorAdapterTest.php | 2 +- .../IlluminatePaginatorAdapterTest.php | 2 +- .../PagerfantaPaginatorAdapterTest.php | 2 +- .../ZendFrameworkPaginatorAdapterTest.php | 2 +- test/Resource/CollectionTest.php | 2 +- test/ScopeTest.php | 2 +- test/Serializer/ArraySerializerTest.php | 2 +- test/Serializer/DataArraySerializerTest.php | 2 +- test/Serializer/JsonApiSerializerTest.php | 4 +-- test/TransformerAbstractTest.php | 2 +- 17 files changed, 19 insertions(+), 80 deletions(-) delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1b05ae84..1e56a205 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,8 +8,8 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-latest] - php: [5.4, 5.5, 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0] + os: [ubuntu-20.04] + php: [7.2, 7.3, 7.4, 8.0] name: League - PHP ${{ matrix.php }} on ${{ matrix.os }} @@ -44,6 +44,3 @@ jobs: - name: Download Scrutinizer phar run: wget https://scrutinizer-ci.com/ocular.phar - - - name: Send Coverage Report to Scrutinizer - run: php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/.gitignore b/.gitignore index aaee0d9c..1cfd6e80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ -.idea -.ruby-version composer.lock build vendor -Gemfile.lock -coverage +.phpunit.result.cache \ No newline at end of file diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 05e77e14..00000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,22 +0,0 @@ -filter: - excluded_paths: [tests, vendor] - -checks: - php: - remove_extra_empty_lines: true - remove_php_closing_tag: true - remove_trailing_whitespace: true - fix_use_statements: - remove_unused: true - preserve_multiple: false - preserve_blanklines: true - order_alphabetically: true - fix_php_opening_tag: true - fix_linefeed: true - fix_line_ending: true - fix_identation_4spaces: true - fix_doc_comments: true - -tools: - external_code_coverage: - timeout: 1800 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8c60cd6a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: php - -sudo: false - -matrix: - include: - - php: 5.4 - dist: trusty - - php: 5.5 - dist: trusty - - php: 5.6 - env: COLLECT_COVERAGE=true - - php: 7.0 - - php: 7.1 - - php: 7.2 - - php: 7.3 - - php: hhvm - dist: trusty - - allow_failures: - - php: hhvm - - -install: - - travis_retry composer install --no-interaction --prefer-source - -script: - - if [[ "$COLLECT_COVERAGE" == "true" ]]; then vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover; else vendor/bin/phpunit --no-coverage; fi - -after_script: - - if [[ "$COLLECT_COVERAGE" == "true" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi \ No newline at end of file diff --git a/README.md b/README.md index 6573ee6b..6e1e2c04 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ [![Latest Version](https://img.shields.io/github/release/thephpleague/fractal.svg?style=flat-square)](https://github.com/thephpleague/fractal/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) ![The PHP League Tests](https://github.com/thephpleague/fractal/workflows/The%20PHP%20League%20Tests/badge.svg) -[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/fractal.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/fractal/code-structure) -[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/fractal.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/fractal) [![Total Downloads](https://img.shields.io/packagist/dt/league/fractal.svg?style=flat-square)](https://packagist.org/packages/league/fractal) Fractal provides a presentation and transformation layer for complex data output, the like found in diff --git a/composer.json b/composer.json index 061efc82..e9de394c 100644 --- a/composer.json +++ b/composer.json @@ -21,15 +21,15 @@ "sort-packages": true }, "require": { - "php": ">=7.0" + "php": ">=7.2" }, "require-dev": { "doctrine/orm": "^2.5", "illuminate/contracts": "~5.0", - "mockery/mockery": "~0.9", + "mockery/mockery": "^1.3", "pagerfanta/pagerfanta": "~1.0.0", - "phpunit/phpunit": "^4.8.35 || ^7.5", - "squizlabs/php_codesniffer": "~1.5|~2.0|~3.4", + "phpunit/phpunit": "^8.5", + "squizlabs/php_codesniffer": "~3.4", "zendframework/zend-paginator": "~2.3" }, "suggest": { diff --git a/test/ManagerTest.php b/test/ManagerTest.php index 475c52af..5f30b913 100755 --- a/test/ManagerTest.php +++ b/test/ManagerTest.php @@ -276,7 +276,7 @@ public function testParseFieldsets() $this->assertSame(null, $manager->getFieldset('inexistent')); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Pagination/DoctrinePaginatorAdapterTest.php b/test/Pagination/DoctrinePaginatorAdapterTest.php index 769d13cb..26ba291e 100644 --- a/test/Pagination/DoctrinePaginatorAdapterTest.php +++ b/test/Pagination/DoctrinePaginatorAdapterTest.php @@ -57,7 +57,7 @@ public function testPaginationAdapter() ); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Pagination/IlluminatePaginatorAdapterTest.php b/test/Pagination/IlluminatePaginatorAdapterTest.php index 4c5b482b..b851ebee 100644 --- a/test/Pagination/IlluminatePaginatorAdapterTest.php +++ b/test/Pagination/IlluminatePaginatorAdapterTest.php @@ -38,7 +38,7 @@ public function testPaginationAdapter() $this->assertSame('http://example.com/foo?page=1', $adapter->getUrl(1)); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Pagination/PagerfantaPaginatorAdapterTest.php b/test/Pagination/PagerfantaPaginatorAdapterTest.php index 0be9f36e..a9a20f74 100644 --- a/test/Pagination/PagerfantaPaginatorAdapterTest.php +++ b/test/Pagination/PagerfantaPaginatorAdapterTest.php @@ -55,7 +55,7 @@ public function testPaginationAdapter() ); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Pagination/ZendFrameworkPaginatorAdapterTest.php b/test/Pagination/ZendFrameworkPaginatorAdapterTest.php index a93208b5..f3574d67 100644 --- a/test/Pagination/ZendFrameworkPaginatorAdapterTest.php +++ b/test/Pagination/ZendFrameworkPaginatorAdapterTest.php @@ -46,7 +46,7 @@ public function testPaginationAdapter() $this->assertSame('http://example.com/foo?page=3', $adapter->getUrl(3)); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Resource/CollectionTest.php b/test/Resource/CollectionTest.php index cd7dbeaa..b5974ac7 100755 --- a/test/Resource/CollectionTest.php +++ b/test/Resource/CollectionTest.php @@ -118,7 +118,7 @@ public function testGetResourceKey() $this->assertSame('foo', $collection->getResourceKey()); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/ScopeTest.php b/test/ScopeTest.php index 3a3cd227..a1e812c8 100755 --- a/test/ScopeTest.php +++ b/test/ScopeTest.php @@ -727,7 +727,7 @@ public function fieldsetsWithSideLoadIncludesProvider() ]; } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Serializer/ArraySerializerTest.php b/test/Serializer/ArraySerializerTest.php index 72d8994b..a6cda7f0 100644 --- a/test/Serializer/ArraySerializerTest.php +++ b/test/Serializer/ArraySerializerTest.php @@ -331,7 +331,7 @@ public function testSerializingCollectionResourceWithoutName() $this->assertSame($expectedJson, $scope->toJson()); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Serializer/DataArraySerializerTest.php b/test/Serializer/DataArraySerializerTest.php index 948aa40f..9780b8e7 100644 --- a/test/Serializer/DataArraySerializerTest.php +++ b/test/Serializer/DataArraySerializerTest.php @@ -355,7 +355,7 @@ public function testSerializingNullResource() $this->assertSame($expected, $scope->toArray()); } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/Serializer/JsonApiSerializerTest.php b/test/Serializer/JsonApiSerializerTest.php index 8c6aad13..f656be11 100644 --- a/test/Serializer/JsonApiSerializerTest.php +++ b/test/Serializer/JsonApiSerializerTest.php @@ -15,7 +15,7 @@ class JsonApiSerializerTest extends TestCase { private $manager; - public function setUp() + public function setUp(): void { $this->manager = new Manager(); $this->manager->setSerializer(new JsonApiSerializer()); @@ -2794,7 +2794,7 @@ public function serializingWithFieldsetsProvider() ]; } - public function tearDown() + public function tearDown(): void { Mockery::close(); } diff --git a/test/TransformerAbstractTest.php b/test/TransformerAbstractTest.php index 56429e8a..bf211fec 100755 --- a/test/TransformerAbstractTest.php +++ b/test/TransformerAbstractTest.php @@ -369,7 +369,7 @@ public function testCollection() $this->assertInstanceOf('League\Fractal\Resource\Collection', $collection); } - public function tearDown() + public function tearDown(): void { m::close(); } From 267bb8f1348615f3bd1dae37c304829e2ddf7244 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:09:11 +0100 Subject: [PATCH 16/32] Run tests on PHP 8.1 (#536) --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1e56a205..da20a864 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-20.04] - php: [7.2, 7.3, 7.4, 8.0] + php: [7.2, 7.3, 7.4, 8.0, 8.1] name: League - PHP ${{ matrix.php }} on ${{ matrix.os }} From 514478c33556985c513f0d99786ead96d890c134 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:22:26 +0100 Subject: [PATCH 17/32] Clean up CI (#537) --- .github/workflows/run-tests.yml | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index da20a864..3a8b7a3c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,23 +24,8 @@ jobs: php-version: ${{ matrix.php }} tools: phpcs, phpunit - - name: Validate Composer - run: composer validate + - name: Download dependencies + uses: ramsey/composer-install@v2 - - name: Cache Dependencies - id: composer-cache - uses: actions/cache@v2 - with: - path: vendor - key: ${{ matrix.os }}-composer-cache-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.os }}-php-${{ matrix.php }} - - - name: Install Dependencies - if: steps.composer-cache.outputs.cache-hit != 'true' - run: composer install --prefer-dist --no-progress --no-suggest - - - name: Execute Tests - run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover - - - name: Download Scrutinizer phar - run: wget https://scrutinizer-ci.com/ocular.phar + - name: Run Tests + run: vendor/bin/phpunit From dbf32a8514a06f6fc0cdbc60039d034dc9a69421 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:22:50 +0100 Subject: [PATCH 18/32] Added .editorconfig (#538) --- .editorconfig | 9 +++++++++ .github/workflows/.editorconfig | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/.editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..677e36e2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.github/workflows/.editorconfig b/.github/workflows/.editorconfig new file mode 100644 index 00000000..7bd3346f --- /dev/null +++ b/.github/workflows/.editorconfig @@ -0,0 +1,2 @@ +[*.yml] +indent_size = 2 From a8813ba3fbe40fa32449a65d2037a8d1c6b6699b Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:23:42 +0100 Subject: [PATCH 19/32] Adding phpstan and psalm (#539) --- .github/workflows/static.yml | 46 ++++++++++++++++++++++++++++++++++++ phpstan-baseline.neon | 2 ++ phpstan.neon.dist | 8 +++++++ psalm.baseline.xml | 2 ++ psalm.xml | 16 +++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 .github/workflows/static.yml create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist create mode 100644 psalm.baseline.xml create mode 100644 psalm.xml diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 00000000..328a6c8f --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,46 @@ +on: [ pull_request ] +name: Static analysis + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-20.04 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: apcu, redis + coverage: none + tools: phpstan:1.4.6, cs2pr + + - name: Download dependencies + uses: ramsey/composer-install@v1 + + - name: PHPStan + run: phpstan analyze --no-progress --error-format=checkstyle | cs2pr + + psalm: + name: Psalm + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: apcu, redis + coverage: none + tools: vimeo/psalm:4.22.0 + + - name: Download dependencies + uses: ramsey/composer-install@v1 + + - name: Psalm + run: psalm --no-progress --output-format=github diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..364905f7 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,2 @@ +parameters: + ignoreErrors: diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..1b347546 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,8 @@ +includes: + - ./phpstan-baseline.neon + +parameters: + level: 5 + reportUnmatchedIgnoredErrors: false + paths: + - src diff --git a/psalm.baseline.xml b/psalm.baseline.xml new file mode 100644 index 00000000..37810d4d --- /dev/null +++ b/psalm.baseline.xml @@ -0,0 +1,2 @@ + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..f03e94f8 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,16 @@ + + + + + + + + + From 594f58bc760aaef02e141fd39025386515cacd71 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:54:06 +0100 Subject: [PATCH 20/32] Fix issues with PHPStan and Psalm (#540) * Psalm fixes * PHPStan fixes * More static fixers * fixes * typo --- src/Manager.php | 6 ++--- src/Pagination/DoctrinePaginatorAdapter.php | 2 +- .../PhalconFrameworkPaginatorAdapter.php | 4 ++-- src/Resource/NullResource.php | 1 + src/Resource/ResourceAbstract.php | 2 +- src/Resource/ResourceInterface.php | 2 +- src/Scope.php | 2 +- src/ScopeFactory.php | 8 +++---- src/Serializer/JsonApiSerializer.php | 22 +++++++++---------- src/TransformerAbstract.php | 4 ++-- 10 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/Manager.php b/src/Manager.php index 4dd4f40c..b1e315ea 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -67,9 +67,7 @@ class Manager protected $recursionLimit = 10; /** - * Serializer. - * - * @var SerializerAbstract + * @var SerializerAbstract|null */ protected $serializer; @@ -147,7 +145,7 @@ public function getRequestedExcludes() public function getSerializer() { if (! $this->serializer) { - $this->setSerializer(new DataArraySerializer()); + $this->serializer = new DataArraySerializer(); } return $this->serializer; diff --git a/src/Pagination/DoctrinePaginatorAdapter.php b/src/Pagination/DoctrinePaginatorAdapter.php index 07d4ef35..2f26c08b 100644 --- a/src/Pagination/DoctrinePaginatorAdapter.php +++ b/src/Pagination/DoctrinePaginatorAdapter.php @@ -51,7 +51,7 @@ public function __construct(Paginator $paginator, callable $routeGenerator) */ public function getCurrentPage() { - return ($this->paginator->getQuery()->getFirstResult() / $this->paginator->getQuery()->getMaxResults()) + 1; + return (int) ($this->paginator->getQuery()->getFirstResult() / $this->paginator->getQuery()->getMaxResults()) + 1; } /** diff --git a/src/Pagination/PhalconFrameworkPaginatorAdapter.php b/src/Pagination/PhalconFrameworkPaginatorAdapter.php index e6f666dd..10ee0889 100644 --- a/src/Pagination/PhalconFrameworkPaginatorAdapter.php +++ b/src/Pagination/PhalconFrameworkPaginatorAdapter.php @@ -30,7 +30,7 @@ class PhalconFrameworkPaginatorAdapter implements PaginatorInterface /** * PhalconFrameworkPaginatorAdapter constructor. * - * @param stdClass $paginator + * @param \stdClass $paginator */ public function __construct($paginator) { @@ -108,6 +108,6 @@ public function getNext() */ public function getUrl($page) { - return $page; + return (string) $page; } } diff --git a/src/Resource/NullResource.php b/src/Resource/NullResource.php index c8396b75..7e27196f 100644 --- a/src/Resource/NullResource.php +++ b/src/Resource/NullResource.php @@ -29,5 +29,6 @@ class NullResource extends ResourceAbstract public function getData() { // Null has no data associated with it. + return null; } } diff --git a/src/Resource/ResourceAbstract.php b/src/Resource/ResourceAbstract.php index 1471837b..e7b6e946 100644 --- a/src/Resource/ResourceAbstract.php +++ b/src/Resource/ResourceAbstract.php @@ -116,7 +116,7 @@ public function getResourceKey() /** * Get the transformer. * - * @return callable|TransformerAbstract + * @return callable|TransformerAbstract|null */ public function getTransformer() { diff --git a/src/Resource/ResourceInterface.php b/src/Resource/ResourceInterface.php index f1322b1f..4e0ef219 100644 --- a/src/Resource/ResourceInterface.php +++ b/src/Resource/ResourceInterface.php @@ -30,7 +30,7 @@ public function getData(); /** * Get the transformer. * - * @return callable|\League\Fractal\TransformerAbstract + * @return callable|\League\Fractal\TransformerAbstract|null */ public function getTransformer(); diff --git a/src/Scope.php b/src/Scope.php index f79d9f39..4a445d92 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -228,7 +228,7 @@ public function setParentScopes($parentScopes) /** * Convert the current data for this scope to an array. * - * @return array + * @return array|null */ public function toArray() { diff --git a/src/ScopeFactory.php b/src/ScopeFactory.php index ccb02e4e..bf7d74db 100644 --- a/src/ScopeFactory.php +++ b/src/ScopeFactory.php @@ -28,22 +28,22 @@ public function createScopeFor(Manager $manager, ResourceInterface $resource, $s /** * @param Manager $manager - * @param Scope $parentScopeInstance + * @param Scope $parentScope * @param ResourceInterface $resource * @param string|null $scopeIdentifier * @return Scope */ public function createChildScopeFor( Manager $manager, - Scope $parentScopeInstance, + Scope $parentScope, ResourceInterface $resource, $scopeIdentifier = null ): Scope { $scopeInstance = $this->createScopeFor($manager, $resource, $scopeIdentifier); // This will be the new children list of parents (parents parents, plus the parent) - $scopeArray = $parentScopeInstance->getParentScopes(); - $scopeArray[] = $parentScopeInstance->getScopeIdentifier(); + $scopeArray = $parentScope->getParentScopes(); + $scopeArray[] = $parentScope->getScopeIdentifier(); $scopeInstance->setParentScopes($scopeArray); diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index 55938dab..f3dcc9da 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -212,13 +212,13 @@ public function sideloadIncludes() /** * @param array $data - * @param array $includedData + * @param array $rawIncludedData * * @return array */ - public function injectData($data, $includedData) + public function injectData($data, $rawIncludedData) { - $relationships = $this->parseRelationships($includedData); + $relationships = $this->parseRelationships($rawIncludedData); if (!empty($relationships)) { $data = $this->fillRelationships($data, $relationships); @@ -432,7 +432,7 @@ protected function shouldIncludeLinks() /** * Check if the objects are part of a collection or not * - * @param $includeObject + * @param array|object $includeObject * * @return array */ @@ -452,7 +452,7 @@ private function createIncludeObjects($includeObject) /** * Sets the RootObjects, either as collection or not. * - * @param $data + * @param array $data */ private function createRootObjects($data) { @@ -467,9 +467,9 @@ private function createRootObjects($data) /** * Loops over the relationships of the provided data and formats it * - * @param $data - * @param $relationship - * @param $key + * @param array $data + * @param array $relationship + * @param string $key * * @return array */ @@ -484,9 +484,9 @@ private function fillRelationshipAsCollection($data, $relationship, $key) /** - * @param $data - * @param $relationship - * @param $key + * @param array $data + * @param array $relationship + * @param string $key * * @return array */ diff --git a/src/TransformerAbstract.php b/src/TransformerAbstract.php index 59d8a8c9..8ccc5caa 100644 --- a/src/TransformerAbstract.php +++ b/src/TransformerAbstract.php @@ -115,7 +115,7 @@ private function figureOutWhichIncludes(Scope $scope) * @param Scope $scope * @param mixed $data * - * @return array + * @return array|false */ public function processIncludedResources(Scope $scope, $data) { @@ -177,7 +177,7 @@ private function includeResourceIfAvailable( * * @throws \Exception * - * @return \League\Fractal\Resource\ResourceInterface + * @return \League\Fractal\Resource\ResourceInterface|false */ protected function callIncludeMethod(Scope $scope, $includeName, $data) { From b9e343ccd77ac75ef9d8fdbc2bb56883bf31967a Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 21:59:41 +0100 Subject: [PATCH 21/32] Support for PHP 81 (#542) * fix: PHP 8.1 support (#1) * fixes Co-authored-by: oprypkhantc <54406427+oprypkhantc@users.noreply.github.com> --- src/Manager.php | 3 ++- src/ParamBag.php | 12 ++++------- src/Scope.php | 3 +-- test/ManagerTest.php | 25 ++++++++--------------- test/ParamBagTest.php | 25 ++++++++--------------- test/ScopeTest.php | 12 +++++------ test/Serializer/JsonApiSerializerTest.php | 9 ++++---- test/TransformerAbstractTest.php | 12 +++++++---- 8 files changed, 43 insertions(+), 58 deletions(-) diff --git a/src/Manager.php b/src/Manager.php index b1e315ea..9dbda9cf 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -176,7 +176,8 @@ public function parseIncludes($includes) foreach ($includes as $include) { list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, ''); - list($allModifiersStr, $subRelations) = array_pad(explode('.', $allModifiersStr, 2), 2, null); + $a = $allModifiersStr ? explode('.', $allModifiersStr, 2) : ['']; + list($allModifiersStr, $subRelations) = array_pad($a, 2, null); // Trim it down to a cool level of recursion $includeName = $this->trimToAcceptableRecursionLevel($includeName); diff --git a/src/ParamBag.php b/src/ParamBag.php index 1810ac3d..a482f931 100644 --- a/src/ParamBag.php +++ b/src/ParamBag.php @@ -105,8 +105,7 @@ public function __unset($key) * * @return bool */ - #[\ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists($key): bool { return $this->__isset($key); } @@ -134,8 +133,7 @@ public function offsetGet($key) * * @return void */ - #[\ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet($key, $value): void { throw new \LogicException('Modifying parameters is not permitted'); } @@ -149,8 +147,7 @@ public function offsetSet($key, $value) * * @return void */ - #[\ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset($key): void { throw new \LogicException('Modifying parameters is not permitted'); } @@ -160,8 +157,7 @@ public function offsetUnset($key) * * @return \ArrayIterator */ - #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->params); } diff --git a/src/Scope.php b/src/Scope.php index 4a445d92..ddc80e16 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -295,8 +295,7 @@ public function toArray() /** * @return mixed */ - #[\ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return $this->toArray(); } diff --git a/test/ManagerTest.php b/test/ManagerTest.php index 5f30b913..5cd99d60 100755 --- a/test/ManagerTest.php +++ b/test/ManagerTest.php @@ -1,5 +1,6 @@ assertInstanceOf(get_class($manager), $manager->parseIncludes(['foo'])); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The parseIncludes() method expects a string or an array. NULL given - */ public function testInvalidParseInclude() { + $this->expectExceptionObject(new InvalidArgumentException('The parseIncludes() method expects a string or an array. NULL given')); + $manager = new Manager(); $manager->parseIncludes(null); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The parseIncludes() method expects a string or an array. integer given - */ public function testIceTParseInclude() { + $this->expectExceptionObject(new InvalidArgumentException('The parseIncludes() method expects a string or an array. integer given')); + $manager = new Manager(); $manager->parseIncludes(99); @@ -99,23 +96,19 @@ public function testParseExcludeSelfie() $this->assertInstanceOf(get_class($manager), $manager->parseExcludes(['foo'])); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The parseExcludes() method expects a string or an array. NULL given - */ public function testInvalidParseExclude() { + $this->expectExceptionObject(new InvalidArgumentException('The parseExcludes() method expects a string or an array. NULL given')); + $manager = new Manager(); $manager->parseExcludes(null); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage The parseExcludes() method expects a string or an array. integer given - */ public function testIceTParseExclude() { + $this->expectExceptionObject(new InvalidArgumentException('The parseExcludes() method expects a string or an array. integer given')); + $manager = new Manager(); $manager->parseExcludes(99); diff --git a/test/ParamBagTest.php b/test/ParamBagTest.php index d63f1fdd..a27c75a4 100644 --- a/test/ParamBagTest.php +++ b/test/ParamBagTest.php @@ -1,6 +1,7 @@ assertNull($params['totallymadeup']); } - /** - * @expectedException \LogicException - * @expectedExceptionMessage Modifying parameters is not permitted - */ public function testArrayAccessSetFails() { + $this->expectExceptionObject(new LogicException('Modifying parameters is not permitted')); + $params = new ParamBag(['foo' => 'bar']); $params['foo'] = 'someothervalue'; } - /** - * @expectedException \LogicException - * @expectedExceptionMessage Modifying parameters is not permitted - */ public function testArrayAccessUnsetFails() { + $this->expectExceptionObject(new LogicException('Modifying parameters is not permitted')); + $params = new ParamBag(['foo' => 'bar']); unset($params['foo']); @@ -64,23 +61,19 @@ public function testObjectAccess() $this->assertTrue(isset($params->foo)); } - /** - * @expectedException \LogicException - * @expectedExceptionMessage Modifying parameters is not permitted - */ public function testObjectAccessSetFails() { + $this->expectExceptionObject(new LogicException('Modifying parameters is not permitted')); + $params = new ParamBag(['foo' => 'bar']); $params->foo = 'someothervalue'; } - /** - * @expectedException \LogicException - * @expectedExceptionMessage Modifying parameters is not permitted - */ public function testObjectAccessUnsetFails() { + $this->expectExceptionObject(new LogicException('Modifying parameters is not permitted')); + $params = new ParamBag(['foo' => 'bar']); unset($params->foo); diff --git a/test/ScopeTest.php b/test/ScopeTest.php index a1e812c8..7282094e 100755 --- a/test/ScopeTest.php +++ b/test/ScopeTest.php @@ -1,5 +1,6 @@ assertTrue($scope->isExcluded('baz.bart')); } - /** - * @expectedException \InvalidArgumentException - */ public function testScopeRequiresConcreteImplementation() { - $manager = new Manager(); + $this->expectException(InvalidArgumentException::class); + + $manager = new Manager(); $manager->parseIncludes('book'); $resource = Mockery::mock('League\Fractal\Resource\ResourceAbstract', [ @@ -382,11 +382,11 @@ public function testRunAppropriateTransformerWithCollection() /** * @covers \League\Fractal\Scope::executeResourceTransformers - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage Argument $resource should be an instance of League\Fractal\Resource\Item or League\Fractal\Resource\Collection */ public function testCreateDataWithClassFuckKnows() { + $this->expectExceptionObject(new InvalidArgumentException('Argument $resource should be an instance of League\Fractal\Resource\Item or League\Fractal\Resource\Collection')); + $manager = new Manager(); $transformer = Mockery::mock('League\Fractal\TransformerAbstract')->makePartial(); diff --git a/test/Serializer/JsonApiSerializerTest.php b/test/Serializer/JsonApiSerializerTest.php index f656be11..b3663ff3 100644 --- a/test/Serializer/JsonApiSerializerTest.php +++ b/test/Serializer/JsonApiSerializerTest.php @@ -1,5 +1,6 @@ assertSame($expectedJson, $scope->toJson()); } - /** - * @expectedException \InvalidArgumentException - * @expectedExceptionMessage JSON API resource objects MUST have a valid id - */ public function testExceptionThrownIfResourceHasNoId() { - $bookData = [ + $this->expectExceptionObject(new InvalidArgumentException('JSON API resource objects MUST have a valid id')); + + $bookData = [ 'title' => 'Foo', 'year' => '1991', ]; diff --git a/test/TransformerAbstractTest.php b/test/TransformerAbstractTest.php index bf211fec..cfe8f6aa 100755 --- a/test/TransformerAbstractTest.php +++ b/test/TransformerAbstractTest.php @@ -1,5 +1,7 @@ expectException(BadMethodCallException::class); + $transformer = m::mock('League\Fractal\TransformerAbstract')->makePartial(); $manager = new Manager(); @@ -116,10 +119,11 @@ public function testProcessEmbeddedResourcesInvalidAvailableEmbed() /** * @covers \League\Fractal\TransformerAbstract::processIncludedResources * @covers \League\Fractal\TransformerAbstract::callIncludeMethod - * @expectedException \BadMethodCallException */ public function testProcessEmbeddedResourcesInvalidDefaultEmbed() { + $this->expectException(BadMethodCallException::class); + $transformer = m::mock('League\Fractal\TransformerAbstract')->makePartial(); $manager = new Manager(); @@ -228,11 +232,11 @@ public function testProcessIncludedAvailableResourcesEmptyEmbed() /** * @covers \League\Fractal\TransformerAbstract::callIncludeMethod - * @expectedException \Exception - * @expectedExceptionMessage Invalid return value from League\Fractal\TransformerAbstract::includeBook(). */ public function testCallEmbedMethodReturnsCrap() { + $this->expectExceptionObject(new Exception('Invalid return value from League\Fractal\TransformerAbstract::includeBook().')); + $manager = new Manager(); $manager->parseIncludes('book'); $transformer = m::mock('League\Fractal\TransformerAbstract[transform]'); From ef870346f6422bd8546a3f6b6181a68c48966481 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 22:00:04 +0100 Subject: [PATCH 22/32] Add ResourceInterface::getMeta() (#541) --- src/Resource/ResourceInterface.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Resource/ResourceInterface.php b/src/Resource/ResourceInterface.php index 4e0ef219..951615c7 100644 --- a/src/Resource/ResourceInterface.php +++ b/src/Resource/ResourceInterface.php @@ -51,4 +51,11 @@ public function setData($data); * @return $this */ public function setTransformer($transformer); + + /** + * Get the meta data. + * + * @return array + */ + public function getMeta(); } From f560f36778b7e8e464cebf39aa9082d68d998c90 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 22:00:50 +0100 Subject: [PATCH 23/32] Added baseline for static analyzers (#543) --- phpstan-baseline.neon | 70 +++++++++++++++++++++++++++++++++++++++++++ psalm.baseline.xml | 24 ++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 364905f7..209483aa 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,2 +1,72 @@ parameters: ignoreErrors: + - + message: "#^Parameter \\#1 \\$separator of function explode expects non\\-empty\\-string, string given\\.$#" + count: 1 + path: src/Manager.php + + - + message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Pagination\\\\LengthAwarePaginator\\:\\:count\\(\\)\\.$#" + count: 1 + path: src/Pagination/IlluminatePaginatorAdapter.php + + - + message: "#^Call to an undefined method League\\\\Fractal\\\\Resource\\\\ResourceInterface\\:\\:getMeta\\(\\)\\.$#" + count: 1 + path: src/Scope.php + + - + message: "#^Call to an undefined method League\\\\Fractal\\\\TransformerAbstract\\:\\:transform\\(\\)\\.$#" + count: 2 + path: src/Scope.php + + - + message: "#^Call to function is_null\\(\\) with array will always evaluate to false\\.$#" + count: 1 + path: src/Scope.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$includeKey\\)\\: Unexpected token \"\\$includeKey\", expected type at offset 18$#" + count: 2 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$includeObject\\)\\: Unexpected token \"\\$includeObject\", expected type at offset 18$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$includeObject\\)\\: Unexpected token \"\\$includeObject\", expected type at offset 73$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$includeObjects\\)\\: Unexpected token \"\\$includeObjects\", expected type at offset 18$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$key\\)\\: Unexpected token \"\\$key\", expected type at offset 102$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$linkedIds\\)\\: Unexpected token \"\\$linkedIds\", expected type at offset 48$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$relationship\\)\\: Unexpected token \"\\$relationship\", expected type at offset 47$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$relationships\\)\\: Unexpected token \"\\$relationships\", expected type at offset 44$#" + count: 2 + path: src/Serializer/JsonApiSerializer.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\$serializedData\\)\\: Unexpected token \"\\$serializedData\", expected type at offset 73$#" + count: 1 + path: src/Serializer/JsonApiSerializer.php + diff --git a/psalm.baseline.xml b/psalm.baseline.xml index 37810d4d..48f5f885 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -1,2 +1,24 @@ - + + + + int + + + $this->paginator->getQuery()->getMaxResults() + + + + + count + + + + + getMeta + + + transform + + + From 5bf32b5fa3717b360d31348793d9fe9cb65fe337 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Mar 2022 23:40:50 +0100 Subject: [PATCH 24/32] Added PHP 7.4 types (#544) * Added type hints * Added all types in src * Fixed type errors in tests * fixess * Fixed testss * cs fixes --- .github/workflows/run-tests.yml | 2 +- composer.json | 4 +- src/Manager.php | 142 ++-------- src/Pagination/Cursor.php | 48 +--- src/Pagination/CursorInterface.php | 7 +- src/Pagination/DoctrinePaginatorAdapter.php | 42 +-- src/Pagination/IlluminatePaginatorAdapter.php | 53 +--- src/Pagination/PagerfantaPaginatorAdapter.php | 69 ++--- src/Pagination/PaginatorInterface.php | 26 +- .../PhalconFrameworkPaginatorAdapter.php | 56 ++-- .../ZendFrameworkPaginatorAdapter.php | 69 ++--- src/ParamBag.php | 42 +-- src/Resource/Collection.php | 55 +--- src/Resource/Item.php | 3 - src/Resource/NullResource.php | 3 +- src/Resource/Primitive.php | 1 - src/Resource/ResourceAbstract.php | 64 ++--- src/Resource/ResourceInterface.php | 17 +- src/Scope.php | 161 +++--------- src/ScopeFactory.php | 22 +- src/ScopeFactoryInterface.php | 19 +- src/Serializer/ArraySerializer.php | 69 ++--- src/Serializer/DataArraySerializer.php | 24 +- src/Serializer/JsonApiSerializer.php | 244 +++++------------- src/Serializer/Serializer.php | 70 ++--- src/Serializer/SerializerAbstract.php | 104 +------- src/TransformerAbstract.php | 89 ++----- test/ScopeTest.php | 6 +- test/Stub/ArraySerializerWithNull.php | 7 +- .../DefaultIncludeBookTransformer.php | 4 +- .../Transformer/GenericBookTransformer.php | 4 +- .../Transformer/JsonApiAuthorTransformer.php | 4 +- .../Transformer/JsonApiBookTransformer.php | 4 +- .../NullIncludeBookTransformer.php | 4 +- .../PrimitiveIncludeBookTransformer.php | 7 +- 35 files changed, 369 insertions(+), 1176 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 3a8b7a3c..f01fdbe3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -9,7 +9,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-20.04] - php: [7.2, 7.3, 7.4, 8.0, 8.1] + php: [7.4, 8.0, 8.1] name: League - PHP ${{ matrix.php }} on ${{ matrix.os }} diff --git a/composer.json b/composer.json index e9de394c..cd5fe828 100644 --- a/composer.json +++ b/composer.json @@ -21,14 +21,14 @@ "sort-packages": true }, "require": { - "php": ">=7.2" + "php": ">=7.4" }, "require-dev": { "doctrine/orm": "^2.5", "illuminate/contracts": "~5.0", "mockery/mockery": "^1.3", "pagerfanta/pagerfanta": "~1.0.0", - "phpunit/phpunit": "^8.5", + "phpunit/phpunit": "^9.5", "squizlabs/php_codesniffer": "~3.4", "zendframework/zend-paginator": "~2.3" }, diff --git a/src/Manager.php b/src/Manager.php index 9dbda9cf..0320bb1e 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -13,7 +13,7 @@ use League\Fractal\Resource\ResourceInterface; use League\Fractal\Serializer\DataArraySerializer; -use League\Fractal\Serializer\SerializerAbstract; +use League\Fractal\Serializer\Serializer; /** * Manager @@ -26,57 +26,40 @@ class Manager { /** * Array of scope identifiers for resources to include. - * - * @var array */ - protected $requestedIncludes = []; + protected array $requestedIncludes = []; /** * Array of scope identifiers for resources to exclude. - * - * @var array */ - protected $requestedExcludes = []; + protected array $requestedExcludes = []; /** * Array of requested fieldsets. - * - * @var array */ - protected $requestedFieldsets = []; + protected array $requestedFieldsets = []; /** * Array containing modifiers as keys and an array value of params. - * - * @var array */ - protected $includeParams = []; + protected array $includeParams = []; /** * The character used to separate modifier parameters. - * - * @var string */ - protected $paramDelimiter = '|'; + protected string $paramDelimiter = '|'; /** * Upper limit to how many levels of included data are allowed. - * - * @var int */ - protected $recursionLimit = 10; + protected int $recursionLimit = 10; - /** - * @var SerializerAbstract|null - */ - protected $serializer; + protected ?Serializer $serializer = null; /** * Factory used to create new configured scopes. - * - * @var ScopeFactoryInterface */ - private $scopeFactory; + private ScopeFactoryInterface $scopeFactory; public function __construct(ScopeFactoryInterface $scopeFactory = null) { @@ -84,18 +67,13 @@ public function __construct(ScopeFactoryInterface $scopeFactory = null) } /** - * Create Data. - * * Main method to kick this all off. Make a resource then pass it over, and use toArray() - * - * @param ResourceInterface $resource - * @param string $scopeIdentifier - * @param Scope $parentScopeInstance - * - * @return Scope */ - public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null) - { + public function createData( + ResourceInterface $resource, + ?string $scopeIdentifier = null, + Scope $parentScopeInstance = null + ): Scope { if ($parentScopeInstance !== null) { return $this->scopeFactory->createChildScopeFor($this, $parentScopeInstance, $resource, $scopeIdentifier); } @@ -103,46 +81,24 @@ public function createData(ResourceInterface $resource, $scopeIdentifier = null, return $this->scopeFactory->createScopeFor($this, $resource, $scopeIdentifier); } - /** - * Get Include Params. - * - * @param string $include - * - * @return \League\Fractal\ParamBag - */ - public function getIncludeParams($include) + public function getIncludeParams(string $include): ParamBag { $params = isset($this->includeParams[$include]) ? $this->includeParams[$include] : []; return new ParamBag($params); } - /** - * Get Requested Includes. - * - * @return array - */ - public function getRequestedIncludes() + public function getRequestedIncludes(): array { return $this->requestedIncludes; } - /** - * Get Requested Excludes. - * - * @return array - */ - public function getRequestedExcludes() + public function getRequestedExcludes(): array { return $this->requestedExcludes; } - /** - * Get Serializer. - * - * @return SerializerAbstract - */ - public function getSerializer() + public function getSerializer(): Serializer { if (! $this->serializer) { $this->serializer = new DataArraySerializer(); @@ -152,13 +108,9 @@ public function getSerializer() } /** - * Parse Include String. - * * @param array|string $includes Array or csv string of resources to include - * - * @return $this */ - public function parseIncludes($includes) + public function parseIncludes($includes): self { // Wipe these before we go again $this->requestedIncludes = $this->includeParams = []; @@ -231,10 +183,8 @@ public function parseIncludes($includes) * @param array $fieldsets Array of fields to include. It must be an array whose keys * are resource types and values an array or a string * of the fields to return, separated by a comma - * - * @return $this */ - public function parseFieldsets(array $fieldsets) + public function parseFieldsets(array $fieldsets): self { $this->requestedFieldsets = []; foreach ($fieldsets as $type => $fields) { @@ -247,25 +197,15 @@ public function parseFieldsets(array $fieldsets) } return $this; } - - /** - * Get requested fieldsets. - * - * @return array - */ - public function getRequestedFieldsets() + public function getRequestedFieldsets(): array { return $this->requestedFieldsets; } /** * Get fieldset params for the specified type. - * - * @param string $type - * - * @return \League\Fractal\ParamBag|null */ - public function getFieldset($type) + public function getFieldset(string $type): ?ParamBag { return !isset($this->requestedFieldsets[$type]) ? null : @@ -273,13 +213,9 @@ public function getFieldset($type) } /** - * Parse Exclude String. - * * @param array|string $excludes Array or csv string of resources to exclude - * - * @return $this */ - public function parseExcludes($excludes) + public function parseExcludes($excludes): self { $this->requestedExcludes = []; @@ -306,28 +242,14 @@ public function parseExcludes($excludes) return $this; } - /** - * Set Recursion Limit. - * - * @param int $recursionLimit - * - * @return $this - */ - public function setRecursionLimit($recursionLimit) + public function setRecursionLimit(int $recursionLimit): self { $this->recursionLimit = $recursionLimit; return $this; } - /** - * Set Serializer - * - * @param SerializerAbstract $serializer - * - * @return $this - */ - public function setSerializer(SerializerAbstract $serializer) + public function setSerializer(Serializer $serializer): self { $this->serializer = $serializer; @@ -335,16 +257,12 @@ public function setSerializer(SerializerAbstract $serializer) } /** - * Auto-include Parents - * * Look at the requested includes and automatically include the parents if they * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz] * * @internal - * - * @return void */ - protected function autoIncludeParents() + protected function autoIncludeParents(): void { $parsed = []; @@ -364,18 +282,12 @@ protected function autoIncludeParents() } /** - * Trim to Acceptable Recursion Level - * * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased * by trains or whatever the hell that movie was about. * * @internal - * - * @param string $includeName - * - * @return string */ - protected function trimToAcceptableRecursionLevel($includeName) + protected function trimToAcceptableRecursionLevel(string $includeName): string { return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit)); } diff --git a/src/Pagination/Cursor.php b/src/Pagination/Cursor.php index 90ca1614..51d41898 100644 --- a/src/Pagination/Cursor.php +++ b/src/Pagination/Cursor.php @@ -42,10 +42,8 @@ class Cursor implements CursorInterface /** * Items being held for the current cursor position. - * - * @var int */ - protected $count; + protected ?int $count; /** * Create a new Cursor instance. @@ -53,11 +51,8 @@ class Cursor implements CursorInterface * @param mixed $current * @param mixed $prev * @param mixed $next - * @param int $count - * - * @return void */ - public function __construct($current = null, $prev = null, $next = null, $count = null) + public function __construct($current = null, $prev = null, $next = null, ?int $count = null) { $this->current = $current; $this->prev = $prev; @@ -66,10 +61,9 @@ public function __construct($current = null, $prev = null, $next = null, $count } /** - * Get the current cursor value. - * - * @return mixed + * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function getCurrent() { return $this->current; @@ -79,10 +73,8 @@ public function getCurrent() * Set the current cursor value. * * @param mixed $current - * - * @return Cursor */ - public function setCurrent($current) + public function setCurrent($current): self { $this->current = $current; @@ -90,10 +82,9 @@ public function setCurrent($current) } /** - * Get the prev cursor value. - * - * @return mixed + * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function getPrev() { return $this->prev; @@ -103,10 +94,8 @@ public function getPrev() * Set the prev cursor value. * * @param mixed $prev - * - * @return Cursor */ - public function setPrev($prev) + public function setPrev($prev): self { $this->prev = $prev; @@ -114,10 +103,9 @@ public function setPrev($prev) } /** - * Get the next cursor value. - * - * @return mixed + * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function getNext() { return $this->next; @@ -127,10 +115,8 @@ public function getNext() * Set the next cursor value. * * @param mixed $next - * - * @return Cursor */ - public function setNext($next) + public function setNext($next): self { $this->next = $next; @@ -138,23 +124,17 @@ public function setNext($next) } /** - * Returns the total items in the current cursor. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): ?int { return $this->count; } /** * Set the total items in the current cursor. - * - * @param int $count - * - * @return Cursor */ - public function setCount($count) + public function setCount(int $count): self { $this->count = $count; diff --git a/src/Pagination/CursorInterface.php b/src/Pagination/CursorInterface.php index 9d6af130..12e1f114 100644 --- a/src/Pagination/CursorInterface.php +++ b/src/Pagination/CursorInterface.php @@ -23,6 +23,7 @@ interface CursorInterface * * @return mixed */ + #[\ReturnTypeWillChange] public function getCurrent(); /** @@ -30,6 +31,7 @@ public function getCurrent(); * * @return mixed */ + #[\ReturnTypeWillChange] public function getPrev(); /** @@ -37,12 +39,11 @@ public function getPrev(); * * @return mixed */ + #[\ReturnTypeWillChange] public function getNext(); /** * Returns the total items in the current cursor. - * - * @return int */ - public function getCount(); + public function getCount(): ?int; } diff --git a/src/Pagination/DoctrinePaginatorAdapter.php b/src/Pagination/DoctrinePaginatorAdapter.php index 2f26c08b..bcab596a 100644 --- a/src/Pagination/DoctrinePaginatorAdapter.php +++ b/src/Pagination/DoctrinePaginatorAdapter.php @@ -45,73 +45,57 @@ public function __construct(Paginator $paginator, callable $routeGenerator) } /** - * Get the current page. - * - * @return int + * {@inheritDoc} */ - public function getCurrentPage() + public function getCurrentPage(): int { return (int) ($this->paginator->getQuery()->getFirstResult() / $this->paginator->getQuery()->getMaxResults()) + 1; } /** - * Get the last page. - * - * @return int + * {@inheritDoc} */ - public function getLastPage() + public function getLastPage(): int { return (int) ceil($this->getTotal() / $this->paginator->getQuery()->getMaxResults()); } /** - * Get the total. - * - * @return int + * {@inheritDoc} */ - public function getTotal() + public function getTotal(): int { return count($this->paginator); } /** - * Get the count. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): int { return $this->paginator->getIterator()->count(); } /** - * Get the number per page. - * - * @return int + * {@inheritDoc} */ - public function getPerPage() + public function getPerPage(): int { return $this->paginator->getQuery()->getMaxResults(); } /** - * Get the url for the given page. - * - * @param int $page - * - * @return string + * {@inheritDoc} */ - public function getUrl($page) + public function getUrl(int $page): string { return call_user_func($this->getRouteGenerator(), $page); } /** * Get the the route generator. - * - * @return callable */ - private function getRouteGenerator() + private function getRouteGenerator(): callable { return $this->routeGenerator; } diff --git a/src/Pagination/IlluminatePaginatorAdapter.php b/src/Pagination/IlluminatePaginatorAdapter.php index 02796e9c..d1f587d7 100644 --- a/src/Pagination/IlluminatePaginatorAdapter.php +++ b/src/Pagination/IlluminatePaginatorAdapter.php @@ -21,19 +21,10 @@ */ class IlluminatePaginatorAdapter implements PaginatorInterface { - /** - * The paginator instance. - * - * @var \Illuminate\Contracts\Pagination\LengthAwarePaginator - */ - protected $paginator; + protected LengthAwarePaginator $paginator; /** * Create a new illuminate pagination adapter. - * - * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator - * - * @return void */ public function __construct(LengthAwarePaginator $paginator) { @@ -41,73 +32,57 @@ public function __construct(LengthAwarePaginator $paginator) } /** - * Get the current page. - * - * @return int + * {@inheritDoc} */ - public function getCurrentPage() + public function getCurrentPage(): int { return $this->paginator->currentPage(); } /** - * Get the last page. - * - * @return int + * {@inheritDoc} */ - public function getLastPage() + public function getLastPage(): int { return $this->paginator->lastPage(); } /** - * Get the total. - * - * @return int + * {@inheritDoc} */ - public function getTotal() + public function getTotal(): int { return $this->paginator->total(); } /** - * Get the count. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): int { return $this->paginator->count(); } /** - * Get the number per page. - * - * @return int + * {@inheritDoc} */ - public function getPerPage() + public function getPerPage(): int { return $this->paginator->perPage(); } /** - * Get the url for the given page. - * - * @param int $page - * - * @return string + * {@inheritDoc} */ - public function getUrl($page) + public function getUrl(int $page): string { return $this->paginator->url($page); } /** * Get the paginator instance. - * - * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator */ - public function getPaginator() + public function getPaginator(): LengthAwarePaginator { return $this->paginator; } diff --git a/src/Pagination/PagerfantaPaginatorAdapter.php b/src/Pagination/PagerfantaPaginatorAdapter.php index 8c652b75..9f593c33 100644 --- a/src/Pagination/PagerfantaPaginatorAdapter.php +++ b/src/Pagination/PagerfantaPaginatorAdapter.php @@ -20,12 +20,7 @@ */ class PagerfantaPaginatorAdapter implements PaginatorInterface { - /** - * The paginator instance. - * - * @var \Pagerfanta\Pagerfanta - */ - protected $paginator; + protected Pagerfanta $paginator; /** * The route generator. @@ -34,98 +29,66 @@ class PagerfantaPaginatorAdapter implements PaginatorInterface */ protected $routeGenerator; - /** - * Create a new pagerfanta pagination adapter. - * - * @param \Pagerfanta\Pagerfanta $paginator - * @param callable $routeGenerator - * - * @return void - */ - public function __construct(Pagerfanta $paginator, $routeGenerator) + public function __construct(Pagerfanta $paginator, callable $routeGenerator) { $this->paginator = $paginator; $this->routeGenerator = $routeGenerator; } /** - * Get the current page. - * - * @return int + * {@inheritDoc} */ - public function getCurrentPage() + public function getCurrentPage(): int { return $this->paginator->getCurrentPage(); } /** - * Get the last page. - * - * @return int + * {@inheritDoc} */ - public function getLastPage() + public function getLastPage(): int { return $this->paginator->getNbPages(); } /** - * Get the total. - * - * @return int + * {@inheritDoc} */ - public function getTotal() + public function getTotal(): int { return count($this->paginator); } /** - * Get the count. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): int { return count($this->paginator->getCurrentPageResults()); } /** - * Get the number per page. - * - * @return int + * {@inheritDoc} */ - public function getPerPage() + public function getPerPage(): int { return $this->paginator->getMaxPerPage(); } /** - * Get the url for the given page. - * - * @param int $page - * - * @return string + * {@inheritDoc} */ - public function getUrl($page) + public function getUrl(int $page): string { return call_user_func($this->routeGenerator, $page); } - /** - * Get the paginator instance. - * - * @return \Pagerfanta\Pagerfanta - */ - public function getPaginator() + public function getPaginator(): Pagerfanta { return $this->paginator; } - /** - * Get the the route generator. - * - * @return callable - */ - public function getRouteGenerator() + public function getRouteGenerator(): callable { return $this->routeGenerator; } diff --git a/src/Pagination/PaginatorInterface.php b/src/Pagination/PaginatorInterface.php index e56a2912..00aabc1a 100644 --- a/src/Pagination/PaginatorInterface.php +++ b/src/Pagination/PaginatorInterface.php @@ -20,45 +20,31 @@ interface PaginatorInterface { /** * Get the current page. - * - * @return int */ - public function getCurrentPage(); + public function getCurrentPage(): int; /** * Get the last page. - * - * @return int */ - public function getLastPage(); + public function getLastPage(): int; /** * Get the total. - * - * @return int */ - public function getTotal(); + public function getTotal(): int; /** * Get the count. - * - * @return int */ - public function getCount(); + public function getCount(): int; /** * Get the number per page. - * - * @return int */ - public function getPerPage(); + public function getPerPage(): int; /** * Get the url for the given page. - * - * @param int $page - * - * @return string */ - public function getUrl($page); + public function getUrl(int $page): string; } diff --git a/src/Pagination/PhalconFrameworkPaginatorAdapter.php b/src/Pagination/PhalconFrameworkPaginatorAdapter.php index 10ee0889..b5dbb9f6 100644 --- a/src/Pagination/PhalconFrameworkPaginatorAdapter.php +++ b/src/Pagination/PhalconFrameworkPaginatorAdapter.php @@ -16,73 +16,55 @@ * * @author Thien Tran * @author Nikolaos Dimopoulos - * */ class PhalconFrameworkPaginatorAdapter implements PaginatorInterface { /** * A slice of the result set to show in the pagination - * - * @var \stdClass */ - private $paginator; + private \stdClass $paginator; - /** - * PhalconFrameworkPaginatorAdapter constructor. - * - * @param \stdClass $paginator - */ - public function __construct($paginator) + public function __construct(\stdClass $paginator) { $this->paginator = $paginator; } /** - * Get the current page. - * - * @return int + * {@inheritDoc} */ - public function getCurrentPage() + public function getCurrentPage(): int { return $this->paginator->current; } /** - * Get the last page. - * - * @return int + * {@inheritDoc} */ - public function getLastPage() + public function getLastPage(): int { return $this->paginator->last; } /** - * Get the total. - * - * @return int + * {@inheritDoc} */ - public function getTotal() + public function getTotal(): int { return $this->paginator->total_items; } /** - * Get the count. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): int { return $this->paginator->total_pages; } /** - * Get the number per page. - * - * @return int + * {@inheritDoc} */ - public function getPerPage() + public function getPerPage(): int { // $this->paginator->items->count() // Because when we use raw sql have not this method @@ -90,23 +72,17 @@ public function getPerPage() } /** - * Get the next. - * - * @return int + * {@inheritDoc} */ - public function getNext() + public function getNext(): int { return $this->paginator->next; } /** - * Get the url for the given page. - * - * @param int $page - * - * @return string + * {@inheritDoc} */ - public function getUrl($page) + public function getUrl(int $page): string { return (string) $page; } diff --git a/src/Pagination/ZendFrameworkPaginatorAdapter.php b/src/Pagination/ZendFrameworkPaginatorAdapter.php index b086c5c6..ed58349f 100644 --- a/src/Pagination/ZendFrameworkPaginatorAdapter.php +++ b/src/Pagination/ZendFrameworkPaginatorAdapter.php @@ -20,12 +20,7 @@ */ class ZendFrameworkPaginatorAdapter implements PaginatorInterface { - /** - * The paginator instance. - * - * @var \Zend\Paginator\Paginator - */ - protected $paginator; + protected Paginator $paginator; /** * The route generator. @@ -34,98 +29,66 @@ class ZendFrameworkPaginatorAdapter implements PaginatorInterface */ protected $routeGenerator; - /** - * Create a new zendframework pagination adapter. - * - * @param \Zend\Paginator\Paginator $paginator - * @param callable $routeGenerator - * - * @return void - */ - public function __construct(Paginator $paginator, $routeGenerator) + public function __construct(Paginator $paginator, callable $routeGenerator) { $this->paginator = $paginator; $this->routeGenerator = $routeGenerator; } /** - * Get the current page. - * - * @return int + * {@inheritDoc} */ - public function getCurrentPage() + public function getCurrentPage(): int { return $this->paginator->getCurrentPageNumber(); } /** - * Get the last page. - * - * @return int + * {@inheritDoc} */ - public function getLastPage() + public function getLastPage(): int { return $this->paginator->count(); } /** - * Get the total. - * - * @return int + * {@inheritDoc} */ - public function getTotal() + public function getTotal(): int { return $this->paginator->getTotalItemCount(); } /** - * Get the count. - * - * @return int + * {@inheritDoc} */ - public function getCount() + public function getCount(): int { return $this->paginator->getCurrentItemCount(); } /** - * Get the number per page. - * - * @return int + * {@inheritDoc} */ - public function getPerPage() + public function getPerPage(): int { return $this->paginator->getItemCountPerPage(); } /** - * Get the url for the given page. - * - * @param int $page - * - * @return string + * {@inheritDoc} */ - public function getUrl($page) + public function getUrl(int $page): string { return call_user_func($this->routeGenerator, $page); } - /** - * Get the paginator instance. - * - * @return \Zend\Paginator\Paginator - */ - public function getPaginator() + public function getPaginator(): Paginator { return $this->paginator; } - /** - * Get the the route generator. - * - * @return callable - */ - public function getRouteGenerator() + public function getRouteGenerator(): callable { return $this->routeGenerator; } diff --git a/src/ParamBag.php b/src/ParamBag.php index a482f931..476fd5bf 100644 --- a/src/ParamBag.php +++ b/src/ParamBag.php @@ -16,17 +16,10 @@ */ class ParamBag implements \ArrayAccess, \IteratorAggregate { - /** - * @var array - */ - protected $params = []; + protected array $params = []; /** * Create a new parameter bag instance. - * - * @param array $params - * - * @return void */ public function __construct(array $params) { @@ -36,11 +29,10 @@ public function __construct(array $params) /** * Get parameter values out of the bag. * - * @param string $key - * * @return mixed */ - public function get($key) + #[\ReturnTypeWillChange] + public function get(string $key) { return $this->__get($key); } @@ -48,23 +40,18 @@ public function get($key) /** * Get parameter values out of the bag via the property access magic method. * - * @param string $key - * * @return mixed */ - public function __get($key) + #[\ReturnTypeWillChange] + public function __get(string $key) { return isset($this->params[$key]) ? $this->params[$key] : null; } /** * Check if a param exists in the bag via an isset() check on the property. - * - * @param string $key - * - * @return bool */ - public function __isset($key) + public function __isset(string $key): bool { return isset($this->params[$key]); } @@ -72,14 +59,11 @@ public function __isset($key) /** * Disallow changing the value of params in the data bag via property access. * - * @param string $key * @param mixed $value * * @throws \LogicException - * - * @return void */ - public function __set($key, $value) + public function __set(string $key, $value): void { throw new \LogicException('Modifying parameters is not permitted'); } @@ -87,13 +71,11 @@ public function __set($key, $value) /** * Disallow unsetting params in the data bag via property access. * - * @param string $key - * * @throws \LogicException * * @return void */ - public function __unset($key) + public function __unset(string $key): void { throw new \LogicException('Modifying parameters is not permitted'); } @@ -102,8 +84,6 @@ public function __unset($key) * Check if a param exists in the bag via an isset() and array access. * * @param string $key - * - * @return bool */ public function offsetExists($key): bool { @@ -130,8 +110,6 @@ public function offsetGet($key) * @param mixed $value * * @throws \LogicException - * - * @return void */ public function offsetSet($key, $value): void { @@ -144,8 +122,6 @@ public function offsetSet($key, $value): void * @param string $key * * @throws \LogicException - * - * @return void */ public function offsetUnset($key): void { @@ -154,8 +130,6 @@ public function offsetUnset($key): void /** * IteratorAggregate for iterating over the object like an array. - * - * @return \ArrayIterator */ public function getIterator(): \ArrayIterator { diff --git a/src/Resource/Collection.php b/src/Resource/Collection.php index 610bb1ae..fd288e7f 100644 --- a/src/Resource/Collection.php +++ b/src/Resource/Collection.php @@ -30,82 +30,47 @@ class Collection extends ResourceAbstract */ protected $data; - /** - * The paginator instance. - * - * @var PaginatorInterface - */ - protected $paginator; + protected ?PaginatorInterface $paginator = null; - /** - * The cursor instance. - * - * @var CursorInterface - */ - protected $cursor; + protected ?CursorInterface $cursor = null; - /** - * Get the paginator instance. - * - * @return PaginatorInterface - */ - public function getPaginator() + public function getPaginator(): ?PaginatorInterface { return $this->paginator; } /** * Determine if the resource has a paginator implementation. - * - * @return bool */ - public function hasPaginator() + public function hasPaginator(): bool { - return $this->paginator instanceof PaginatorInterface; + return $this->paginator !== null; } /** * Get the cursor instance. - * - * @return CursorInterface */ - public function getCursor() + public function getCursor(): ?CursorInterface { return $this->cursor; } /** * Determine if the resource has a cursor implementation. - * - * @return bool */ - public function hasCursor() + public function hasCursor(): bool { - return $this->cursor instanceof CursorInterface; + return $this->cursor !== null; } - /** - * Set the paginator instance. - * - * @param PaginatorInterface $paginator - * - * @return $this - */ - public function setPaginator(PaginatorInterface $paginator) + public function setPaginator(PaginatorInterface $paginator): self { $this->paginator = $paginator; return $this; } - /** - * Set the cursor instance. - * - * @param CursorInterface $cursor - * - * @return $this - */ - public function setCursor(CursorInterface $cursor) + public function setCursor(CursorInterface $cursor): self { $this->cursor = $cursor; diff --git a/src/Resource/Item.php b/src/Resource/Item.php index a1df70be..a1b05765 100644 --- a/src/Resource/Item.php +++ b/src/Resource/Item.php @@ -12,13 +12,10 @@ namespace League\Fractal\Resource; /** - * Item Resource - * * The Item Resource can store any mixed data, usually an ORM, ODM or * other sort of intelligent result, DataMapper model, etc but could * be a basic array, object, or whatever you like. */ class Item extends ResourceAbstract { - // } diff --git a/src/Resource/NullResource.php b/src/Resource/NullResource.php index 7e27196f..d153584a 100644 --- a/src/Resource/NullResource.php +++ b/src/Resource/NullResource.php @@ -12,8 +12,6 @@ namespace League\Fractal\Resource; /** - * Null Resource - * * The Null Resource represents a resource that doesn't exist. This can be * useful to indicate that a certain relationship is null in some output * formats (e.g. JSON API), which require even a relationship that is null at @@ -26,6 +24,7 @@ class NullResource extends ResourceAbstract * * @return mixed */ + #[\ReturnTypeWillChange] public function getData() { // Null has no data associated with it. diff --git a/src/Resource/Primitive.php b/src/Resource/Primitive.php index c0bedd61..dd512733 100644 --- a/src/Resource/Primitive.php +++ b/src/Resource/Primitive.php @@ -19,5 +19,4 @@ */ class Primitive extends ResourceAbstract { - // } diff --git a/src/Resource/ResourceAbstract.php b/src/Resource/ResourceAbstract.php index e7b6e946..a6864fde 100644 --- a/src/Resource/ResourceAbstract.php +++ b/src/Resource/ResourceAbstract.php @@ -24,17 +24,13 @@ abstract class ResourceAbstract implements ResourceInterface /** * Array of meta data. - * - * @var array */ - protected $meta = []; + protected array $meta = []; /** * The resource key. - * - * @var string */ - protected $resourceKey; + protected ?string $resourceKey; /** * A callable to process the data attached to this resource. @@ -44,13 +40,10 @@ abstract class ResourceAbstract implements ResourceInterface protected $transformer; /** - * Create a new resource instance. - * * @param mixed $data * @param callable|TransformerAbstract|null $transformer - * @param string $resourceKey */ - public function __construct($data = null, $transformer = null, $resourceKey = null) + public function __construct($data = null, $transformer = null, ?string $resourceKey = null) { $this->data = $data; $this->transformer = $transformer; @@ -62,6 +55,7 @@ public function __construct($data = null, $transformer = null, $resourceKey = nu * * @return mixed */ + #[\ReturnTypeWillChange] public function getData() { return $this->data; @@ -71,10 +65,8 @@ public function getData() * Set the data. * * @param mixed $data - * - * @return $this */ - public function setData($data) + public function setData($data): self { $this->data = $data; @@ -83,10 +75,8 @@ public function setData($data) /** * Get the meta data. - * - * @return array */ - public function getMeta() + public function getMeta(): array { return $this->meta; } @@ -94,23 +84,17 @@ public function getMeta() /** * Get the meta data. * - * @param string $metaKey - * - * @return array + * @return mixed */ - public function getMetaValue($metaKey) + #[\ReturnTypeWillChange] + public function getMetaValue(string $metaKey) { return $this->meta[$metaKey]; } - /** - * Get the resource key. - * - * @return string - */ - public function getResourceKey() + public function getResourceKey(): string { - return $this->resourceKey; + return $this->resourceKey ?? ''; } /** @@ -127,10 +111,8 @@ public function getTransformer() * Set the transformer. * * @param callable|TransformerAbstract $transformer - * - * @return $this */ - public function setTransformer($transformer) + public function setTransformer($transformer): self { $this->transformer = $transformer; @@ -139,12 +121,8 @@ public function setTransformer($transformer) /** * Set the meta data. - * - * @param array $meta - * - * @return $this */ - public function setMeta(array $meta) + public function setMeta(array $meta): self { $this->meta = $meta; @@ -152,28 +130,18 @@ public function setMeta(array $meta) } /** - * Set the meta data. + * Set one meta data value. * - * @param string $metaKey * @param mixed $metaValue - * - * @return $this */ - public function setMetaValue($metaKey, $metaValue) + public function setMetaValue(string $metaKey, $metaValue): self { $this->meta[$metaKey] = $metaValue; return $this; } - /** - * Set the resource key. - * - * @param string $resourceKey - * - * @return $this - */ - public function setResourceKey($resourceKey) + public function setResourceKey(string $resourceKey): self { $this->resourceKey = $resourceKey; diff --git a/src/Resource/ResourceInterface.php b/src/Resource/ResourceInterface.php index 951615c7..dc8b8751 100644 --- a/src/Resource/ResourceInterface.php +++ b/src/Resource/ResourceInterface.php @@ -15,16 +15,15 @@ interface ResourceInterface { /** * Get the resource key. - * - * @return string */ - public function getResourceKey(); + public function getResourceKey(): string; /** * Get the data. * * @return mixed */ + #[\ReturnTypeWillChange] public function getData(); /** @@ -38,24 +37,18 @@ public function getTransformer(); * Set the data. * * @param mixed $data - * - * @return $this */ - public function setData($data); + public function setData($data): self; /** * Set the transformer. * * @param callable|\League\Fractal\TransformerAbstract $transformer - * - * @return $this */ - public function setTransformer($transformer); + public function setTransformer($transformer): self; /** * Get the meta data. - * - * @return array */ - public function getMeta(); + public function getMeta(): array; } diff --git a/src/Scope.php b/src/Scope.php index ddc80e16..e823b9f3 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -17,7 +17,7 @@ use League\Fractal\Resource\Primitive; use League\Fractal\Resource\NullResource; use League\Fractal\Resource\ResourceInterface; -use League\Fractal\Serializer\SerializerAbstract; +use League\Fractal\Serializer\Serializer; /** * Scope @@ -28,41 +28,17 @@ */ class Scope implements \JsonSerializable { - /** - * @var array - */ - protected $availableIncludes = []; + protected array $availableIncludes = []; - /** - * @var string - */ - protected $scopeIdentifier; + protected ?string $scopeIdentifier; - /** - * @var \League\Fractal\Manager - */ - protected $manager; + protected Manager $manager; - /** - * @var ResourceInterface - */ - protected $resource; + protected ResourceInterface $resource; - /** - * @var array - */ - protected $parentScopes = []; + protected array $parentScopes = []; - /** - * Create a new scope instance. - * - * @param Manager $manager - * @param ResourceInterface $resource - * @param string $scopeIdentifier - * - * @return void - */ - public function __construct(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null) + public function __construct(Manager $manager, ResourceInterface $resource, ?string $scopeIdentifier = null) { $this->manager = $manager; $this->resource = $resource; @@ -73,35 +49,24 @@ public function __construct(Manager $manager, ResourceInterface $resource, $scop * Embed a scope as a child of the current scope. * * @internal - * - * @param string $scopeIdentifier - * @param ResourceInterface $resource - * - * @return \League\Fractal\Scope */ - public function embedChildScope($scopeIdentifier, $resource) + public function embedChildScope(string $scopeIdentifier, ResourceInterface $resource): Scope { return $this->manager->createData($resource, $scopeIdentifier, $this); } /** * Get the current identifier. - * - * @return string */ - public function getScopeIdentifier() + public function getScopeIdentifier(): ?string { return $this->scopeIdentifier; } /** * Get the unique identifier for this scope. - * - * @param string $appendIdentifier - * - * @return string */ - public function getIdentifier($appendIdentifier = null) + public function getIdentifier(?string $appendIdentifier = null): string { $identifierParts = array_merge($this->parentScopes, [$this->scopeIdentifier, $appendIdentifier]); @@ -109,49 +74,34 @@ public function getIdentifier($appendIdentifier = null) } /** - * Getter for parentScopes. - * * @return mixed */ + #[\ReturnTypeWillChange] public function getParentScopes() { return $this->parentScopes; } - /** - * Getter for resource. - * - * @return ResourceInterface - */ - public function getResource() + public function getResource(): ResourceInterface { return $this->resource; } - /** - * Getter for manager. - * - * @return \League\Fractal\Manager - */ - public function getManager() + public function getManager(): Manager { return $this->manager; } /** - * Is Requested. - * * Check if - in relation to the current scope - this specific segment is allowed. * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current * scope is a then c is not allowed, even if it is there and potentially transformable. * * @internal * - * @param string $checkScopeSegment - * * @return bool Returns the new number of elements in the array. */ - public function isRequested($checkScopeSegment) + public function isRequested(string $checkScopeSegment): bool { if ($this->parentScopes) { $scopeArray = array_slice($this->parentScopes, 1); @@ -160,26 +110,20 @@ public function isRequested($checkScopeSegment) $scopeArray = [$checkScopeSegment]; } - $scopeString = implode('.', (array) $scopeArray); + $scopeString = implode('.', $scopeArray); return in_array($scopeString, $this->manager->getRequestedIncludes()); } /** - * Is Excluded. - * * Check if - in relation to the current scope - this specific segment should * be excluded. That means, if a.b.c is excluded and the current scope is a.b, * then c will not be allowed in the transformation whether it appears in * the list of default or available, requested includes. * * @internal - * - * @param string $checkScopeSegment - * - * @return bool */ - public function isExcluded($checkScopeSegment) + public function isExcluded(string $checkScopeSegment): bool { if ($this->parentScopes) { $scopeArray = array_slice($this->parentScopes, 1); @@ -188,7 +132,7 @@ public function isExcluded($checkScopeSegment) $scopeArray = [$checkScopeSegment]; } - $scopeString = implode('.', (array) $scopeArray); + $scopeString = implode('.', $scopeArray); return in_array($scopeString, $this->manager->getRequestedExcludes()); } @@ -200,11 +144,9 @@ public function isExcluded($checkScopeSegment) * * @internal * - * @param string $identifierSegment - * * @return int Returns the new number of elements in the array. */ - public function pushParentScope($identifierSegment) + public function pushParentScope(string $identifierSegment): int { return array_push($this->parentScopes, $identifierSegment); } @@ -215,10 +157,8 @@ public function pushParentScope($identifierSegment) * @internal * * @param string[] $parentScopes Value to set. - * - * @return $this */ - public function setParentScopes($parentScopes) + public function setParentScopes(array $parentScopes): self { $this->parentScopes = $parentScopes; @@ -227,10 +167,8 @@ public function setParentScopes($parentScopes) /** * Convert the current data for this scope to an array. - * - * @return array|null */ - public function toArray() + public function toArray(): ?array { list($rawData, $rawIncludedData) = $this->executeResourceTransformers(); @@ -295,21 +233,18 @@ public function toArray() /** * @return mixed */ - public function jsonSerialize(): array + #[\ReturnTypeWillChange] + public function jsonSerialize() { return $this->toArray(); } - /** + /** * Convert the current data for this scope to JSON. - * - * @param int $options - * - * @return string */ - public function toJson($options = 0) + public function toJson(int $options = 0): string { - return json_encode($this, $options); + return \json_encode($this, $options); } /** @@ -317,6 +252,7 @@ public function toJson($options = 0) * * @return mixed */ + #[\ReturnTypeWillChange] public function transformPrimitiveResource() { if (! ($this->resource instanceof Primitive)) { @@ -344,10 +280,8 @@ public function transformPrimitiveResource() * Execute the resources transformer and return the data and included data. * * @internal - * - * @return array */ - protected function executeResourceTransformers() + protected function executeResourceTransformers(): array { $transformer = $this->resource->getTransformer(); $data = $this->resource->getData(); @@ -378,12 +312,9 @@ protected function executeResourceTransformers() * * @internal * - * @param SerializerAbstract $serializer - * @param mixed $data - * - * @return array + * @param mixed $data */ - protected function serializeResource(SerializerAbstract $serializer, $data) + protected function serializeResource(Serializer $serializer, $data): ?array { $resourceKey = $this->resource->getResourceKey(); @@ -405,10 +336,8 @@ protected function serializeResource(SerializerAbstract $serializer, $data) * * @param TransformerAbstract|callable $transformer * @param mixed $data - * - * @return array */ - protected function fireTransformer($transformer, $data) + protected function fireTransformer($transformer, $data): array { $includedData = []; @@ -437,10 +366,8 @@ protected function fireTransformer($transformer, $data) * * @param \League\Fractal\TransformerAbstract $transformer * @param mixed $data - * - * @return array */ - protected function fireIncludedTransformers($transformer, $data) + protected function fireIncludedTransformers($transformer, $data): array { $this->availableIncludes = $transformer->getAvailableIncludes(); @@ -453,10 +380,8 @@ protected function fireIncludedTransformers($transformer, $data) * @internal * * @param TransformerAbstract|callable $transformer - * - * @return bool */ - protected function transformerHasIncludes($transformer) + protected function transformerHasIncludes($transformer): bool { if (! $transformer instanceof TransformerAbstract) { return false; @@ -470,10 +395,8 @@ protected function transformerHasIncludes($transformer) /** * Check, if this is the root scope. - * - * @return bool */ - protected function isRootScope() + protected function isRootScope(): bool { return empty($this->parentScopes); } @@ -483,12 +406,8 @@ protected function isRootScope() * the scope resource * * @internal - * - * @param array $data - * - * @return array */ - protected function filterFieldsets(array $data) + protected function filterFieldsets(array $data): array { if (!$this->hasFilterFieldset()) { return $data; @@ -509,10 +428,8 @@ protected function filterFieldsets(array $data) * Return the requested filter fieldset for the scope resource * * @internal - * - * @return \League\Fractal\ParamBag|null */ - protected function getFilterFieldset() + protected function getFilterFieldset(): ?ParamBag { return $this->manager->getFieldset($this->getResourceType()); } @@ -521,10 +438,8 @@ protected function getFilterFieldset() * Check if there are requested filter fieldsets for the scope resource. * * @internal - * - * @return bool */ - protected function hasFilterFieldset() + protected function hasFilterFieldset(): bool { return $this->getFilterFieldset() !== null; } @@ -533,10 +448,8 @@ protected function hasFilterFieldset() * Return the scope resource type. * * @internal - * - * @return string */ - protected function getResourceType() + protected function getResourceType(): string { return $this->resource->getResourceKey(); } diff --git a/src/ScopeFactory.php b/src/ScopeFactory.php index bf7d74db..5e719321 100644 --- a/src/ScopeFactory.php +++ b/src/ScopeFactory.php @@ -15,29 +15,19 @@ class ScopeFactory implements ScopeFactoryInterface { - /** - * @param Manager $manager - * @param ResourceInterface $resource - * @param string|null $scopeIdentifier - * @return Scope - */ - public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null): Scope - { + public function createScopeFor( + Manager $manager, + ResourceInterface $resource, + ?string $scopeIdentifier = null + ): Scope { return new Scope($manager, $resource, $scopeIdentifier); } - /** - * @param Manager $manager - * @param Scope $parentScope - * @param ResourceInterface $resource - * @param string|null $scopeIdentifier - * @return Scope - */ public function createChildScopeFor( Manager $manager, Scope $parentScope, ResourceInterface $resource, - $scopeIdentifier = null + ?string $scopeIdentifier = null ): Scope { $scopeInstance = $this->createScopeFor($manager, $resource, $scopeIdentifier); diff --git a/src/ScopeFactoryInterface.php b/src/ScopeFactoryInterface.php index fe247c6d..fe4fa680 100644 --- a/src/ScopeFactoryInterface.php +++ b/src/ScopeFactoryInterface.php @@ -14,35 +14,20 @@ use League\Fractal\Resource\ResourceInterface; /** - * ScopeFactoryInterface - * * Creates Scope Instances for resources */ interface ScopeFactoryInterface { - /** - * @param Manager $manager - * @param ResourceInterface $resource - * @param string|null $scopeIdentifier - * @return Scope - */ public function createScopeFor( Manager $manager, ResourceInterface $resource, - $scopeIdentifier = null + ?string $scopeIdentifier = null ): Scope; - /** - * @param Manager $manager - * @param Scope $parentScope - * @param ResourceInterface $resource - * @param string|null $scopeIdentifier - * @return Scope - */ public function createChildScopeFor( Manager $manager, Scope $parentScope, ResourceInterface $resource, - $scopeIdentifier = null + ?string $scopeIdentifier = null ): Scope; } diff --git a/src/Serializer/ArraySerializer.php b/src/Serializer/ArraySerializer.php index 52489603..cfe90631 100644 --- a/src/Serializer/ArraySerializer.php +++ b/src/Serializer/ArraySerializer.php @@ -18,62 +18,41 @@ class ArraySerializer extends SerializerAbstract { /** - * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function collection($resourceKey, array $data) + public function collection(string $resourceKey, array $data): array { return [$resourceKey ?: 'data' => $data]; } /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function item($resourceKey, array $data) + public function item(string $resourceKey, array $data): array { return $data; } /** - * Serialize null resource. - * - * @return array + * {@inheritDoc} */ - public function null() + public function null(): ?array { return []; } /** - * Serialize the included data. - * - * @param ResourceInterface $resource - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function includedData(ResourceInterface $resource, array $data) + public function includedData(ResourceInterface $resource, array $data): array { return $data; } /** - * Serialize the meta. - * - * @param array $meta - * - * @return array + * {@inheritDoc} */ - public function meta(array $meta) + public function meta(array $meta): array { if (empty($meta)) { return []; @@ -83,21 +62,17 @@ public function meta(array $meta) } /** - * Serialize the paginator. - * - * @param PaginatorInterface $paginator - * - * @return array + * {@inheritDoc} */ - public function paginator(PaginatorInterface $paginator) + public function paginator(PaginatorInterface $paginator): array { - $currentPage = (int) $paginator->getCurrentPage(); - $lastPage = (int) $paginator->getLastPage(); + $currentPage = $paginator->getCurrentPage(); + $lastPage = $paginator->getLastPage(); $pagination = [ - 'total' => (int) $paginator->getTotal(), - 'count' => (int) $paginator->getCount(), - 'per_page' => (int) $paginator->getPerPage(), + 'total' => $paginator->getTotal(), + 'count' => $paginator->getCount(), + 'per_page' => $paginator->getPerPage(), 'current_page' => $currentPage, 'total_pages' => $lastPage, ]; @@ -120,19 +95,15 @@ public function paginator(PaginatorInterface $paginator) } /** - * Serialize the cursor. - * - * @param CursorInterface $cursor - * - * @return array + * {@inheritDoc} */ - public function cursor(CursorInterface $cursor) + public function cursor(CursorInterface $cursor): array { $cursor = [ 'current' => $cursor->getCurrent(), 'prev' => $cursor->getPrev(), 'next' => $cursor->getNext(), - 'count' => (int) $cursor->getCount(), + 'count' => $cursor->getCount(), ]; return ['cursor' => $cursor]; diff --git a/src/Serializer/DataArraySerializer.php b/src/Serializer/DataArraySerializer.php index 70339833..65cd4069 100644 --- a/src/Serializer/DataArraySerializer.php +++ b/src/Serializer/DataArraySerializer.php @@ -14,37 +14,25 @@ class DataArraySerializer extends ArraySerializer { /** - * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function collection($resourceKey, array $data) + public function collection(string $resourceKey, array $data): array { return ['data' => $data]; } /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function item($resourceKey, array $data) + public function item(string $resourceKey, array $data): array { return ['data' => $data]; } /** - * Serialize null resource. - * - * @return array + * {@inheritDoc} */ - public function null() + public function null(): ?array { return ['data' => []]; } diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index f3dcc9da..97b662ea 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -17,29 +17,18 @@ class JsonApiSerializer extends ArraySerializer { - protected $baseUrl; - protected $rootObjects; + protected ?string $baseUrl = null; + protected array $rootObjects = []; - /** - * JsonApiSerializer constructor. - * - * @param string $baseUrl - */ - public function __construct($baseUrl = null) + public function __construct(?string $baseUrl = null) { $this->baseUrl = $baseUrl; - $this->rootObjects = []; } /** - * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function collection($resourceKey, array $data) + public function collection(string $resourceKey, array $data): array { $resources = []; @@ -51,14 +40,9 @@ public function collection($resourceKey, array $data) } /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function item($resourceKey, array $data) + public function item(string $resourceKey, array $data): array { $id = $this->getIdFromData($data); @@ -99,21 +83,17 @@ public function item($resourceKey, array $data) } /** - * Serialize the paginator. - * - * @param PaginatorInterface $paginator - * - * @return array + * {@inheritDoc} */ - public function paginator(PaginatorInterface $paginator) + public function paginator(PaginatorInterface $paginator): array { - $currentPage = (int)$paginator->getCurrentPage(); - $lastPage = (int)$paginator->getLastPage(); + $currentPage = $paginator->getCurrentPage(); + $lastPage = $paginator->getLastPage(); $pagination = [ - 'total' => (int)$paginator->getTotal(), - 'count' => (int)$paginator->getCount(), - 'per_page' => (int)$paginator->getPerPage(), + 'total' => $paginator->getTotal(), + 'count' => $paginator->getCount(), + 'per_page' => $paginator->getPerPage(), 'current_page' => $currentPage, 'total_pages' => $lastPage, ]; @@ -137,13 +117,9 @@ public function paginator(PaginatorInterface $paginator) } /** - * Serialize the meta. - * - * @param array $meta - * - * @return array + * {@inheritDoc} */ - public function meta(array $meta) + public function meta(array $meta): array { if (empty($meta)) { return []; @@ -160,9 +136,9 @@ public function meta(array $meta) } /** - * @return array + * {@inheritDoc} */ - public function null() + public function null(): ?array { return [ 'data' => null, @@ -170,14 +146,9 @@ public function null() } /** - * Serialize the included data. - * - * @param ResourceInterface $resource - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function includedData(ResourceInterface $resource, array $data) + public function includedData(ResourceInterface $resource, array $data): array { list($serializedData, $linkedIds) = $this->pullOutNestedIncludedData($data); @@ -201,22 +172,17 @@ public function includedData(ResourceInterface $resource, array $data) } /** - * Indicates if includes should be side-loaded. - * - * @return bool + * {@inheritDoc} */ - public function sideloadIncludes() + public function sideloadIncludes(): bool { return true; } /** - * @param array $data - * @param array $rawIncludedData - * - * @return array + * {@inheritDoc} */ - public function injectData($data, $rawIncludedData) + public function injectData(array $data, array $rawIncludedData): array { $relationships = $this->parseRelationships($rawIncludedData); @@ -228,18 +194,15 @@ public function injectData($data, $rawIncludedData) } /** + * {@inheritDoc} + * * Hook to manipulate the final sideloaded includes. * The JSON API specification does not allow the root object to be included * into the sideloaded `included`-array. We have to make sure it is * filtered out, in case some object links to the root object in a * relationship. - * - * @param array $includedData - * @param array $data - * - * @return array */ - public function filterIncludes($includedData, $data) + public function filterIncludes(array $includedData, array $data): array { if (!isset($includedData['included'])) { return $includedData; @@ -258,33 +221,25 @@ public function filterIncludes($includedData, $data) } /** - * Get the mandatory fields for the serializer - * - * @return array + * {@inheritDoc} */ - public function getMandatoryFields() + public function getMandatoryFields(): array { return ['id']; } /** * Filter function to delete root objects from array. - * - * @param array $object - * - * @return bool */ - protected function filterRootObject($object) + protected function filterRootObject(array $object): bool { return !$this->isRootObject($object); } /** * Set the root objects of the JSON API tree. - * - * @param array $objects */ - protected function setRootObjects(array $objects = []) + protected function setRootObjects(array $objects = []): void { $this->rootObjects = array_map(function ($object) { return "{$object['type']}:{$object['id']}"; @@ -293,55 +248,31 @@ protected function setRootObjects(array $objects = []) /** * Determines whether an object is a root object of the JSON API tree. - * - * @param array $object - * - * @return bool */ - protected function isRootObject($object) + protected function isRootObject(array $object): bool { $objectKey = "{$object['type']}:{$object['id']}"; + return in_array($objectKey, $this->rootObjects); } - /** - * @param array $data - * - * @return bool - */ - protected function isCollection($data) + protected function isCollection(array $data): bool { return array_key_exists('data', $data) && - array_key_exists(0, $data['data']); + array_key_exists(0, $data['data']); } - /** - * @param array $data - * - * @return bool - */ - protected function isNull($data) + protected function isNull(array $data): bool { return array_key_exists('data', $data) && $data['data'] === null; } - /** - * @param array $data - * - * @return bool - */ - protected function isEmpty($data) + protected function isEmpty(array $data): bool { return array_key_exists('data', $data) && $data['data'] === []; } - /** - * @param array $data - * @param array $relationships - * - * @return array - */ - protected function fillRelationships($data, $relationships) + protected function fillRelationships(array $data, array $relationships): array { if ($this->isCollection($data)) { foreach ($relationships as $key => $relationship) { @@ -356,12 +287,7 @@ protected function fillRelationships($data, $relationships) return $data; } - /** - * @param array $includedData - * - * @return array - */ - protected function parseRelationships($includedData) + protected function parseRelationships(array $includedData): array { $relationships = []; @@ -378,10 +304,9 @@ protected function parseRelationships($includedData) } /** - * @param array $data - * - * @return integer + * @return mixed */ + #[\ReturnTypeWillChange] protected function getIdFromData(array $data) { if (!array_key_exists('id', $data)) { @@ -389,17 +314,14 @@ protected function getIdFromData(array $data) 'JSON API resource objects MUST have a valid id' ); } + return $data['id']; } /** * Keep all sideloaded inclusion data on the top level. - * - * @param array $data - * - * @return array */ - protected function pullOutNestedIncludedData(array $data) + protected function pullOutNestedIncludedData(array $data): array { $includedData = []; $linkedIds = []; @@ -421,10 +343,8 @@ protected function pullOutNestedIncludedData(array $data) /** * Whether or not the serializer should include `links` for resource objects. - * - * @return bool */ - protected function shouldIncludeLinks() + protected function shouldIncludeLinks(): bool { return $this->baseUrl !== null; } @@ -433,10 +353,8 @@ protected function shouldIncludeLinks() * Check if the objects are part of a collection or not * * @param array|object $includeObject - * - * @return array */ - private function createIncludeObjects($includeObject) + private function createIncludeObjects($includeObject): array { if ($this->isCollection($includeObject)) { $includeObjects = $includeObject['data']; @@ -451,10 +369,8 @@ private function createIncludeObjects($includeObject) /** * Sets the RootObjects, either as collection or not. - * - * @param array $data */ - private function createRootObjects($data) + private function createRootObjects(array $data): void { if ($this->isCollection($data)) { $this->setRootObjects($data['data']); @@ -466,14 +382,8 @@ private function createRootObjects($data) /** * Loops over the relationships of the provided data and formats it - * - * @param array $data - * @param array $relationship - * @param string $key - * - * @return array */ - private function fillRelationshipAsCollection($data, $relationship, $key) + private function fillRelationshipAsCollection(array $data, array $relationship, string $key): array { foreach ($relationship as $index => $relationshipData) { $data['data'][$index]['relationships'][$key] = $relationshipData; @@ -482,31 +392,19 @@ private function fillRelationshipAsCollection($data, $relationship, $key) return $data; } - - /** - * @param array $data - * @param array $relationship - * @param string $key - * - * @return array - */ - private function fillRelationshipAsSingleResource($data, $relationship, $key) + private function fillRelationshipAsSingleResource(array $data, array $relationship, string $key): array { $data['data']['relationships'][$key] = $relationship[0]; return $data; } - /** - * @param $includeKey - * @param $relationships - * @param $includeObject - * @param $key - * - * @return array - */ - private function buildRelationships($includeKey, $relationships, $includeObject, $key) - { + private function buildRelationships( + string $includeKey, + array $relationships, + array $includeObject, + string $key + ): array { $relationships = $this->addIncludekeyToRelationsIfNotSet($includeKey, $relationships); if ($this->isNull($includeObject)) { @@ -533,13 +431,7 @@ private function buildRelationships($includeKey, $relationships, $includeObject, return $relationships; } - /** - * @param $includeKey - * @param $relationships - * - * @return array - */ - private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships) + private function addIncludekeyToRelationsIfNotSet(string $includeKey, array $relationships): array { if (!array_key_exists($includeKey, $relationships)) { $relationships[$includeKey] = []; @@ -549,13 +441,7 @@ private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships) return $relationships; } - /** - * @param $includeObject - * @param $relationship - * - * @return array - */ - private function addIncludedDataToRelationship($includeObject, $relationship) + private function addIncludedDataToRelationship(array $includeObject, array $relationship): array { foreach ($includeObject['data'] as $object) { $relationship['data'][] = [ @@ -570,7 +456,7 @@ private function addIncludedDataToRelationship($includeObject, $relationship) /** * {@inheritdoc} */ - public function injectAvailableIncludeData($data, $availableIncludes) + public function injectAvailableIncludeData(array $data, array $availableIncludes): array { if (!$this->shouldIncludeLinks()) { return $data; @@ -598,7 +484,7 @@ public function injectAvailableIncludeData($data, $availableIncludes) * @param array $resource The resource to add relationship links to * @param string $relationshipKey The resource key of the relationship */ - private function addRelationshipLinks($resource, $relationshipKey) + private function addRelationshipLinks(array $resource, string $relationshipKey): array { if (!isset($resource['relationships']) || !isset($resource['relationships'][$relationshipKey])) { $resource['relationships'][$relationshipKey] = []; @@ -617,15 +503,11 @@ private function addRelationshipLinks($resource, $relationshipKey) return $resource; } - /** - * @param $includeObjects - * @param $linkedIds - * @param $serializedData - * - * @return array - */ - private function serializeIncludedObjectsWithCacheKey($includeObjects, $linkedIds, $serializedData) - { + private function serializeIncludedObjectsWithCacheKey( + array $includeObjects, + array $linkedIds, + array $serializedData + ): array { foreach ($includeObjects as $object) { $includeType = $object['type']; $includeId = $object['id']; diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php index e3252b08..d194a90e 100644 --- a/src/Serializer/Serializer.php +++ b/src/Serializer/Serializer.php @@ -9,94 +9,60 @@ interface Serializer { /** * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * - * @return array */ - public function collection($resourceKey, array $data); + public function collection(string $resourceKey, array $data): array; /** * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array */ - public function item($resourceKey, array $data); + public function item(string $resourceKey, array $data): array; /** * Serialize null resource. - * - * @return array */ - public function null(); + public function null(): ?array; /** * Serialize the included data. - * - * @param ResourceInterface $resource - * @param array $data - * - * @return array */ - public function includedData(ResourceInterface $resource, array $data); + public function includedData(ResourceInterface $resource, array $data): array; /** * Serialize the meta. - * - * @param array $meta - * - * @return array */ - public function meta(array $meta); + public function meta(array $meta): array; /** * Serialize the paginator. - * - * @param PaginatorInterface $paginator - * - * @return array */ - public function paginator(PaginatorInterface $paginator); + public function paginator(PaginatorInterface $paginator): array; /** * Serialize the cursor. - * - * @param CursorInterface $cursor - * - * @return array */ - public function cursor(CursorInterface $cursor); + public function cursor(CursorInterface $cursor): array; - public function mergeIncludes($transformedData, $includedData); + public function mergeIncludes(array $transformedData, array $includedData): array; + + public function injectAvailableIncludeData(array $data, array $availableIncludes): array; /** * Indicates if includes should be side-loaded. - * - * @return bool */ - public function sideloadIncludes(); + public function sideloadIncludes(): bool; /** * Hook for the serializer to inject custom data based on the relationships of the resource. - * - * @param array $data - * @param array $rawIncludedData - * - * @return array */ - public function injectData($data, $rawIncludedData); + public function injectData(array $data, array $rawIncludedData): array; /** * Hook for the serializer to modify the final list of includes. - * - * @param array $includedData - * @param array $data - * - * @return array */ - public function filterIncludes($includedData, $data); + public function filterIncludes(array $includedData, array $data): array; + + /** + * Get the mandatory fields for the serializer + */ + public function getMandatoryFields(): array; } diff --git a/src/Serializer/SerializerAbstract.php b/src/Serializer/SerializerAbstract.php index 909c46c0..faadc2aa 100644 --- a/src/Serializer/SerializerAbstract.php +++ b/src/Serializer/SerializerAbstract.php @@ -18,70 +18,9 @@ abstract class SerializerAbstract implements Serializer { /** - * Serialize a collection. - * - * @param string $resourceKey - * @param array $data - * - * @return array + * {@inheritDoc} */ - abstract public function collection($resourceKey, array $data); - - /** - * Serialize an item. - * - * @param string $resourceKey - * @param array $data - * - * @return array - */ - abstract public function item($resourceKey, array $data); - - /** - * Serialize null resource. - * - * @return array - */ - abstract public function null(); - - /** - * Serialize the included data. - * - * @param ResourceInterface $resource - * @param array $data - * - * @return array - */ - abstract public function includedData(ResourceInterface $resource, array $data); - - /** - * Serialize the meta. - * - * @param array $meta - * - * @return array - */ - abstract public function meta(array $meta); - - /** - * Serialize the paginator. - * - * @param PaginatorInterface $paginator - * - * @return array - */ - abstract public function paginator(PaginatorInterface $paginator); - - /** - * Serialize the cursor. - * - * @param CursorInterface $cursor - * - * @return array - */ - abstract public function cursor(CursorInterface $cursor); - - public function mergeIncludes($transformedData, $includedData) + public function mergeIncludes(array $transformedData, array $includedData): array { // If the serializer does not want the includes to be side-loaded then // the included data must be merged with the transformed data. @@ -93,60 +32,41 @@ public function mergeIncludes($transformedData, $includedData) } /** - * Indicates if includes should be side-loaded. - * - * @return bool + * {@inheritDoc} */ - public function sideloadIncludes() + public function sideloadIncludes(): bool { return false; } /** - * Hook for the serializer to inject custom data based on the relationships of the resource. - * - * @param array $data - * @param array $rawIncludedData - * - * @return array + * {@inheritDoc} */ - public function injectData($data, $rawIncludedData) + public function injectData(array $data, array $rawIncludedData): array { return $data; } /** - * Hook for the serializer to inject custom data based on the available includes of the resource. - * - * @param array $data - * @param array $availableIncludes - * - * @return array + * {@inheritDoc} */ - public function injectAvailableIncludeData($data, $availableIncludes) + public function injectAvailableIncludeData(array $data, array $availableIncludes): array { return $data; } /** - * Hook for the serializer to modify the final list of includes. - * - * @param array $includedData - * @param array $data - * - * @return array + * {@inheritDoc} */ - public function filterIncludes($includedData, $data) + public function filterIncludes(array $includedData, array $data): array { return $includedData; } /** - * Get the mandatory fields for the serializer - * - * @return array + * {@inheritDoc} */ - public function getMandatoryFields() + public function getMandatoryFields(): array { return []; } diff --git a/src/TransformerAbstract.php b/src/TransformerAbstract.php index 8ccc5caa..94f05614 100644 --- a/src/TransformerAbstract.php +++ b/src/TransformerAbstract.php @@ -18,8 +18,6 @@ use League\Fractal\Resource\ResourceInterface; /** - * Transformer Abstract - * * All Transformer classes should extend this to utilize the convenience methods * collection() and item(), and make the self::$availableIncludes property available. * Extend it and add a `transform()` method to transform any default or included data @@ -29,65 +27,47 @@ abstract class TransformerAbstract { /** * Resources that can be included if requested. - * - * @var array */ - protected $availableIncludes = []; + protected array $availableIncludes = []; /** * Include resources without needing it to be requested. - * - * @var array */ - protected $defaultIncludes = []; + protected array $defaultIncludes = []; /** * The transformer should know about the current scope, so we can fetch relevant params. - * - * @var Scope */ - protected $currentScope; + protected ?Scope $currentScope; /** * Getter for availableIncludes. - * - * @return array */ - public function getAvailableIncludes() + public function getAvailableIncludes(): array { return $this->availableIncludes; } /** * Getter for defaultIncludes. - * - * @return array */ - public function getDefaultIncludes() + public function getDefaultIncludes(): array { return $this->defaultIncludes; } /** * Getter for currentScope. - * - * @return \League\Fractal\Scope */ - public function getCurrentScope() + public function getCurrentScope(): ?Scope { return $this->currentScope; } /** * Figure out which includes we need. - * - * @internal - * - * @param Scope $scope - * - * @return array */ - private function figureOutWhichIncludes(Scope $scope) + private function figureOutWhichIncludes(Scope $scope): array { $includes = $this->getDefaultIncludes(); @@ -112,7 +92,6 @@ private function figureOutWhichIncludes(Scope $scope) * * @internal * - * @param Scope $scope * @param mixed $data * * @return array|false @@ -138,21 +117,14 @@ public function processIncludedResources(Scope $scope, $data) /** * Include a resource only if it is available on the method. * - * @internal - * - * @param Scope $scope * @param mixed $data - * @param array $includedData - * @param string $include - * - * @return array */ private function includeResourceIfAvailable( Scope $scope, $data, - $includedData, - $include - ) { + array $includedData, + string $include + ): array { if ($resource = $this->callIncludeMethod($scope, $include, $data)) { $childScope = $scope->embedChildScope($include, $resource); @@ -171,15 +143,13 @@ private function includeResourceIfAvailable( * * @internal * - * @param Scope $scope - * @param string $includeName * @param mixed $data * * @throws \Exception * * @return \League\Fractal\Resource\ResourceInterface|false */ - protected function callIncludeMethod(Scope $scope, $includeName, $data) + protected function callIncludeMethod(Scope $scope, string $includeName, $data) { $scopeIdentifier = $scope->getIdentifier($includeName); @@ -221,12 +191,8 @@ protected function callIncludeMethod(Scope $scope, $includeName, $data) /** * Setter for availableIncludes. - * - * @param array $availableIncludes - * - * @return $this */ - public function setAvailableIncludes($availableIncludes) + public function setAvailableIncludes(array $availableIncludes): self { $this->availableIncludes = $availableIncludes; @@ -235,12 +201,8 @@ public function setAvailableIncludes($availableIncludes) /** * Setter for defaultIncludes. - * - * @param array $defaultIncludes - * - * @return $this */ - public function setDefaultIncludes($defaultIncludes) + public function setDefaultIncludes(array $defaultIncludes): self { $this->defaultIncludes = $defaultIncludes; @@ -249,12 +211,8 @@ public function setDefaultIncludes($defaultIncludes) /** * Setter for currentScope. - * - * @param Scope $currentScope - * - * @return $this */ - public function setCurrentScope($currentScope) + public function setCurrentScope(Scope $currentScope): self { $this->currentScope = $currentScope; @@ -266,11 +224,8 @@ public function setCurrentScope($currentScope) * * @param mixed $data * @param callable|null $transformer - * @param string $resourceKey - * - * @return Primitive */ - protected function primitive($data, $transformer = null, $resourceKey = null) + protected function primitive($data, ?callable $transformer = null, ?string $resourceKey = null): Primitive { return new Primitive($data, $transformer, $resourceKey); } @@ -280,11 +235,8 @@ protected function primitive($data, $transformer = null, $resourceKey = null) * * @param mixed $data * @param TransformerAbstract|callable $transformer - * @param string $resourceKey - * - * @return Item */ - protected function item($data, $transformer, $resourceKey = null) + protected function item($data, $transformer, ?string $resourceKey = null): Item { return new Item($data, $transformer, $resourceKey); } @@ -294,21 +246,16 @@ protected function item($data, $transformer, $resourceKey = null) * * @param mixed $data * @param TransformerAbstract|callable $transformer - * @param string $resourceKey - * - * @return Collection */ - protected function collection($data, $transformer, $resourceKey = null) + protected function collection($data, $transformer, ?string $resourceKey = null): Collection { return new Collection($data, $transformer, $resourceKey); } /** * Create a new null resource object. - * - * @return NullResource */ - protected function null() + protected function null(): NullResource { return new NullResource(); } diff --git a/test/ScopeTest.php b/test/ScopeTest.php index 7282094e..41abca3b 100755 --- a/test/ScopeTest.php +++ b/test/ScopeTest.php @@ -332,7 +332,7 @@ public function testRunAppropriateTransformerWithPrimitive() $transformer = Mockery::mock('League\Fractal\TransformerAbstract'); $transformer->shouldReceive('transform')->once()->andReturn('simple string'); - $transformer->shouldReceive('setCurrentScope')->once()->andReturn([]); + $transformer->shouldReceive('setCurrentScope')->once()->andReturnSelf(); $transformer->shouldNotReceive('getAvailableIncludes'); $transformer->shouldNotReceive('getDefaultIncludes'); @@ -355,7 +355,7 @@ public function testRunAppropriateTransformerWithItem() $transformer->shouldReceive('transform')->once()->andReturn($this->simpleItem); $transformer->shouldReceive('getAvailableIncludes')->once()->andReturn([]); $transformer->shouldReceive('getDefaultIncludes')->once()->andReturn([]); - $transformer->shouldReceive('setCurrentScope')->once()->andReturn([]); + $transformer->shouldReceive('setCurrentScope')->once()->andReturnSelf(); $resource = new Item($this->simpleItem, $transformer); $scope = $manager->createData($resource); @@ -371,7 +371,7 @@ public function testRunAppropriateTransformerWithCollection() $transformer->shouldReceive('transform')->once()->andReturn(['foo' => 'bar']); $transformer->shouldReceive('getAvailableIncludes')->once()->andReturn([]); $transformer->shouldReceive('getDefaultIncludes')->once()->andReturn([]); - $transformer->shouldReceive('setCurrentScope')->once()->andReturn([]); + $transformer->shouldReceive('setCurrentScope')->once()->andReturnSelf(); $resource = new Collection([['foo' => 'bar']], $transformer); $scope = $manager->createData($resource); diff --git a/test/Stub/ArraySerializerWithNull.php b/test/Stub/ArraySerializerWithNull.php index 9943ae61..7b5074eb 100644 --- a/test/Stub/ArraySerializerWithNull.php +++ b/test/Stub/ArraySerializerWithNull.php @@ -7,12 +7,7 @@ class ArraySerializerWithNull extends ArraySerializer { - /** - * Serialize null resource. - * - * @return null - */ - public function null() + public function null(): ?array { return null; } diff --git a/test/Stub/Transformer/DefaultIncludeBookTransformer.php b/test/Stub/Transformer/DefaultIncludeBookTransformer.php index be85b8f6..7c874179 100644 --- a/test/Stub/Transformer/DefaultIncludeBookTransformer.php +++ b/test/Stub/Transformer/DefaultIncludeBookTransformer.php @@ -4,11 +4,11 @@ class DefaultIncludeBookTransformer extends TransformerAbstract { - protected $defaultIncludes = [ + protected array $defaultIncludes = [ 'author', ]; - public function transform() + public function transform(): array { return ['a' => 'b']; } diff --git a/test/Stub/Transformer/GenericBookTransformer.php b/test/Stub/Transformer/GenericBookTransformer.php index 633ed508..65a409d9 100644 --- a/test/Stub/Transformer/GenericBookTransformer.php +++ b/test/Stub/Transformer/GenericBookTransformer.php @@ -4,11 +4,11 @@ class GenericBookTransformer extends TransformerAbstract { - protected $availableIncludes = [ + protected array $availableIncludes = [ 'author', ]; - public function transform(array $book) + public function transform(array $book): array { $book['year'] = (int) $book['year']; unset($book['_author']); diff --git a/test/Stub/Transformer/JsonApiAuthorTransformer.php b/test/Stub/Transformer/JsonApiAuthorTransformer.php index a6c3df4c..e04c6ac7 100644 --- a/test/Stub/Transformer/JsonApiAuthorTransformer.php +++ b/test/Stub/Transformer/JsonApiAuthorTransformer.php @@ -4,11 +4,11 @@ class JsonApiAuthorTransformer extends TransformerAbstract { - protected $availableIncludes = [ + protected array $availableIncludes = [ 'published', ]; - public function transform(array $author) + public function transform(array $author): array { unset($author['_published']); diff --git a/test/Stub/Transformer/JsonApiBookTransformer.php b/test/Stub/Transformer/JsonApiBookTransformer.php index 600b529f..46af3f17 100644 --- a/test/Stub/Transformer/JsonApiBookTransformer.php +++ b/test/Stub/Transformer/JsonApiBookTransformer.php @@ -4,13 +4,13 @@ class JsonApiBookTransformer extends TransformerAbstract { - protected $availableIncludes = [ + protected array $availableIncludes = [ 'author', 'co-author', 'author-with-meta', ]; - public function transform(array $book) + public function transform(array $book): array { $book['year'] = (int) $book['year']; unset($book['_author']); diff --git a/test/Stub/Transformer/NullIncludeBookTransformer.php b/test/Stub/Transformer/NullIncludeBookTransformer.php index 49d7c4c8..28012490 100644 --- a/test/Stub/Transformer/NullIncludeBookTransformer.php +++ b/test/Stub/Transformer/NullIncludeBookTransformer.php @@ -4,11 +4,11 @@ class NullIncludeBookTransformer extends TransformerAbstract { - protected $defaultIncludes = [ + protected array $defaultIncludes = [ 'author', ]; - public function transform() + public function transform(): array { return ['a' => 'b']; } diff --git a/test/Stub/Transformer/PrimitiveIncludeBookTransformer.php b/test/Stub/Transformer/PrimitiveIncludeBookTransformer.php index 894613f5..f3d0fb69 100644 --- a/test/Stub/Transformer/PrimitiveIncludeBookTransformer.php +++ b/test/Stub/Transformer/PrimitiveIncludeBookTransformer.php @@ -1,19 +1,20 @@ 'b']; } - public function includePrice(array $book) + public function includePrice(array $book): Primitive { return $this->primitive($book['price'], function ($price) {return (int) $price;}); } From 419b0cbf5c23a06886a583c2fc0530db2360a70f Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Tue, 8 Mar 2022 00:12:17 +0100 Subject: [PATCH 25/32] Add stale bot (#545) --- .github/workflows/stale.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..004297ab --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,13 @@ +daysUntilStale: 180 +daysUntilClose: 28 +exemptLabels: + - keep-open +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed after 4 weeks if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false From c3ae2a0514690f85c503d79fd19bca379d22250c Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Tue, 8 Mar 2022 17:49:55 +0100 Subject: [PATCH 26/32] Update supported versions in the readme (#546) --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index 6e1e2c04..82054f44 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,7 @@ $ composer require league/fractal The following versions of PHP are supported by this version. -* PHP 5.4 -* PHP 5.5 -* PHP 5.6 -* PHP 7.0 -* PHP 7.1 -* PHP 7.2 -* HHVM +* >= PHP 7.4 ## Documentation From 94fbef8a1b453462ca77e2ce58a86e22ee7753e7 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Tue, 8 Mar 2022 21:33:52 +0100 Subject: [PATCH 27/32] Fix markdown (#547) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 82054f44..a69cea2b 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ $ composer require league/fractal ## Requirements -The following versions of PHP are supported by this version. +The following versions of PHP are supported by this version: -* >= PHP 7.4 +>= PHP 7.4 ## Documentation From 87ed8916ee9b63cee71f2690d55074bb6fd414ae Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 9 Mar 2022 14:31:56 +0100 Subject: [PATCH 28/32] Move stale config to the correct location (#548) --- .github/{workflows => }/stale.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{workflows => }/stale.yml (100%) diff --git a/.github/workflows/stale.yml b/.github/stale.yml similarity index 100% rename from .github/workflows/stale.yml rename to .github/stale.yml From 71f98626a2a035c4f75740cf912ec06ffb02b030 Mon Sep 17 00:00:00 2001 From: Matt Trask Date: Wed, 9 Mar 2022 07:59:44 -0600 Subject: [PATCH 29/32] add psalm and phpstan to lib (#549) --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index cd5fe828..540a7c92 100644 --- a/composer.json +++ b/composer.json @@ -28,8 +28,10 @@ "illuminate/contracts": "~5.0", "mockery/mockery": "^1.3", "pagerfanta/pagerfanta": "~1.0.0", + "phpstan/phpstan": "^1.4", "phpunit/phpunit": "^9.5", "squizlabs/php_codesniffer": "~3.4", + "vimeo/psalm": "^4.22", "zendframework/zend-paginator": "~2.3" }, "suggest": { From 94ce1fd3f4b60d32db63200543251269c36e52e8 Mon Sep 17 00:00:00 2001 From: Matt Trask Date: Mon, 11 Apr 2022 07:47:06 -0500 Subject: [PATCH 30/32] allow for null values to be passed to Scope (#553) * allow for null values to be passed to Scope * nullable params * nullable for jsonapi serializer * add test coverage for resource key issue Co-authored-by: = <=> --- src/Scope.php | 13 +++- src/Serializer/ArraySerializer.php | 4 +- src/Serializer/DataArraySerializer.php | 4 +- src/Serializer/JsonApiSerializer.php | 4 +- src/Serializer/Serializer.php | 4 +- test/Serializer/CustomArraySerializerTest.php | 76 +++++++++++++++++++ test/Serializer/DataArraySerializerTest.php | 11 +++ test/Stub/Serializer/RootSerializer.php | 32 ++++++++ .../GenericBookNoResourceKeyTransformer.php | 29 +++++++ 9 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 test/Serializer/CustomArraySerializerTest.php create mode 100644 test/Stub/Serializer/RootSerializer.php create mode 100644 test/Stub/Transformer/GenericBookNoResourceKeyTransformer.php diff --git a/src/Scope.php b/src/Scope.php index e823b9f3..45037b66 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -319,11 +319,20 @@ protected function serializeResource(Serializer $serializer, $data): ?array $resourceKey = $this->resource->getResourceKey(); if ($this->resource instanceof Collection) { - return $serializer->collection($resourceKey, $data); + if (!empty($resourceKey)) { + return $serializer->collection($resourceKey, $data); + } + + return $serializer->collection(null, $data); } if ($this->resource instanceof Item) { - return $serializer->item($resourceKey, $data); + // this is where it breaks now. + if (!empty($resourceKey)) { + return $serializer->item($resourceKey, $data); + } + + return $serializer->item(null, $data); } return $serializer->null(); diff --git a/src/Serializer/ArraySerializer.php b/src/Serializer/ArraySerializer.php index cfe90631..1b620008 100644 --- a/src/Serializer/ArraySerializer.php +++ b/src/Serializer/ArraySerializer.php @@ -20,7 +20,7 @@ class ArraySerializer extends SerializerAbstract /** * {@inheritDoc} */ - public function collection(string $resourceKey, array $data): array + public function collection(?string $resourceKey, array $data): array { return [$resourceKey ?: 'data' => $data]; } @@ -28,7 +28,7 @@ public function collection(string $resourceKey, array $data): array /** * {@inheritDoc} */ - public function item(string $resourceKey, array $data): array + public function item(?string $resourceKey, array $data): array { return $data; } diff --git a/src/Serializer/DataArraySerializer.php b/src/Serializer/DataArraySerializer.php index 65cd4069..2f382ef8 100644 --- a/src/Serializer/DataArraySerializer.php +++ b/src/Serializer/DataArraySerializer.php @@ -16,7 +16,7 @@ class DataArraySerializer extends ArraySerializer /** * {@inheritDoc} */ - public function collection(string $resourceKey, array $data): array + public function collection(?string $resourceKey, array $data): array { return ['data' => $data]; } @@ -24,7 +24,7 @@ public function collection(string $resourceKey, array $data): array /** * {@inheritDoc} */ - public function item(string $resourceKey, array $data): array + public function item(?string $resourceKey, array $data): array { return ['data' => $data]; } diff --git a/src/Serializer/JsonApiSerializer.php b/src/Serializer/JsonApiSerializer.php index 97b662ea..e532d010 100644 --- a/src/Serializer/JsonApiSerializer.php +++ b/src/Serializer/JsonApiSerializer.php @@ -28,7 +28,7 @@ public function __construct(?string $baseUrl = null) /** * {@inheritDoc} */ - public function collection(string $resourceKey, array $data): array + public function collection(?string $resourceKey, array $data): array { $resources = []; @@ -42,7 +42,7 @@ public function collection(string $resourceKey, array $data): array /** * {@inheritDoc} */ - public function item(string $resourceKey, array $data): array + public function item(?string $resourceKey, array $data): array { $id = $this->getIdFromData($data); diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php index d194a90e..cb3e07d9 100644 --- a/src/Serializer/Serializer.php +++ b/src/Serializer/Serializer.php @@ -10,12 +10,12 @@ interface Serializer /** * Serialize a collection. */ - public function collection(string $resourceKey, array $data): array; + public function collection(?string $resourceKey, array $data): array; /** * Serialize an item. */ - public function item(string $resourceKey, array $data): array; + public function item(?string $resourceKey, array $data): array; /** * Serialize null resource. diff --git a/test/Serializer/CustomArraySerializerTest.php b/test/Serializer/CustomArraySerializerTest.php new file mode 100644 index 00000000..f1b5b962 --- /dev/null +++ b/test/Serializer/CustomArraySerializerTest.php @@ -0,0 +1,76 @@ +parseIncludes('author'); + $manager->setSerializer(new RootSerializer()); + + $bookData = [ + 'title' => 'Foo', + 'year' => '1991', + '_author' => [ + 'name' => 'Dave', + ], + ]; + + $resource = new Item($bookData, new GenericBookNoResourceKeyTransformer(), 'data'); + $scope = new Scope($manager, $resource); + + $expected = [ + 'data' => [ + 'title' => 'Foo', + 'year' => 1991, + 'author' => [ + 'name' => 'Dave', + ], + ], + ]; + + $this->assertSame($expected, $scope->toArray()); + } + + public function testMismatchedResourceKey() + { + $manager = new Manager(); + $manager->parseIncludes('author'); + $manager->setSerializer(new RootSerializer()); + + $bookData = [ + 'title' => 'Foo', + 'year' => '1991', + '_author' => [ + 'name' => 'Dave', + ], + ]; + + $resource = new Item($bookData, new GenericBookNoResourceKeyTransformer(), 'data'); + $scope = new Scope($manager, $resource); + + $expected = [ + 'data' => [ + 'title' => 'Foo', + 'year' => 1991, + 'author' => [ + 'data' => [ + 'name' => 'Dave', + ] + ], + ], + ]; + + $this->assertNotSame($expected, $scope->toArray()); + } +} diff --git a/test/Serializer/DataArraySerializerTest.php b/test/Serializer/DataArraySerializerTest.php index 9780b8e7..8e9eb88e 100644 --- a/test/Serializer/DataArraySerializerTest.php +++ b/test/Serializer/DataArraySerializerTest.php @@ -6,6 +6,8 @@ use League\Fractal\Resource\NullResource; use League\Fractal\Scope; use League\Fractal\Serializer\DataArraySerializer; +use League\Fractal\Test\Stub\Serializer\RootSerializer; +use League\Fractal\Test\Stub\Transformer\GenericBookNoResourceKeyTransformer; use League\Fractal\Test\Stub\Transformer\GenericBookTransformer; use Mockery; use PHPUnit\Framework\TestCase; @@ -355,6 +357,15 @@ public function testSerializingNullResource() $this->assertSame($expected, $scope->toArray()); } + public function testCanPassNullValueToSerializer() + { + $testClass = new \stdClass(); + $testClass->name = 'test'; + $testClass->email = 'test@test.com'; + + + } + public function tearDown(): void { Mockery::close(); diff --git a/test/Stub/Serializer/RootSerializer.php b/test/Stub/Serializer/RootSerializer.php new file mode 100644 index 00000000..fe04c519 --- /dev/null +++ b/test/Stub/Serializer/RootSerializer.php @@ -0,0 +1,32 @@ + $data]; + } + + /** + * Serialize an item. + * + * @param string $resourceKey + * @param array $data + * @return array + */ + public function item($resourceKey, array $data): array + { + return is_null($resourceKey) ? $data : [$resourceKey => $data]; + } +} diff --git a/test/Stub/Transformer/GenericBookNoResourceKeyTransformer.php b/test/Stub/Transformer/GenericBookNoResourceKeyTransformer.php new file mode 100644 index 00000000..b18e3a1a --- /dev/null +++ b/test/Stub/Transformer/GenericBookNoResourceKeyTransformer.php @@ -0,0 +1,29 @@ +item($book['_author'], new GenericAuthorTransformer()); + } +} From 8b9d39b67624db9195c06f9c1ffd0355151eaf62 Mon Sep 17 00:00:00 2001 From: David Pugh Date: Mon, 11 Apr 2022 13:47:17 +0100 Subject: [PATCH 31/32] add test case for accessing current scope before its value has been set (#552) --- src/TransformerAbstract.php | 2 +- test/TransformerAbstractTest.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/TransformerAbstract.php b/src/TransformerAbstract.php index 94f05614..5464077e 100644 --- a/src/TransformerAbstract.php +++ b/src/TransformerAbstract.php @@ -38,7 +38,7 @@ abstract class TransformerAbstract /** * The transformer should know about the current scope, so we can fetch relevant params. */ - protected ?Scope $currentScope; + protected ?Scope $currentScope = null; /** * Getter for availableIncludes. diff --git a/test/TransformerAbstractTest.php b/test/TransformerAbstractTest.php index cfe8f6aa..6a705e4a 100755 --- a/test/TransformerAbstractTest.php +++ b/test/TransformerAbstractTest.php @@ -74,6 +74,16 @@ public function testGetCurrentScope() $this->assertSame($transformer->getCurrentScope(), $scope); } + /** + * @covers \League\Fractal\TransformerAbstract::getCurrentScope + */ + public function testCanAccessScopeBeforeInitialization() + { + $transformer = $this->getMockForAbstractClass('League\Fractal\TransformerAbstract'); + $currentScope = $transformer->getCurrentScope(); + $this->assertNull($currentScope); + } + public function testProcessEmbeddedResourcesNoAvailableIncludes() { $transformer = m::mock('League\Fractal\TransformerAbstract')->makePartial(); From f1e8f3200e2a606ef6cf90314d40a982135540bb Mon Sep 17 00:00:00 2001 From: Korvin Szanto Date: Sat, 16 Apr 2022 18:55:44 +0000 Subject: [PATCH 32/32] Fix tests --- phpstan-baseline.neon | 68 +--------------------- psalm.baseline.xml | 13 ----- psalm.xml | 2 +- src/Pagination/SimplePaginationAdapter.php | 27 +++------ src/Resource/MetaDataInterface.php | 7 +-- src/Serializer/Serializer.php | 1 + src/TransformerAbstract.php | 16 ++++- 7 files changed, 28 insertions(+), 106 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 209483aa..eea23caa 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3,70 +3,4 @@ parameters: - message: "#^Parameter \\#1 \\$separator of function explode expects non\\-empty\\-string, string given\\.$#" count: 1 - path: src/Manager.php - - - - message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Pagination\\\\LengthAwarePaginator\\:\\:count\\(\\)\\.$#" - count: 1 - path: src/Pagination/IlluminatePaginatorAdapter.php - - - - message: "#^Call to an undefined method League\\\\Fractal\\\\Resource\\\\ResourceInterface\\:\\:getMeta\\(\\)\\.$#" - count: 1 - path: src/Scope.php - - - - message: "#^Call to an undefined method League\\\\Fractal\\\\TransformerAbstract\\:\\:transform\\(\\)\\.$#" - count: 2 - path: src/Scope.php - - - - message: "#^Call to function is_null\\(\\) with array will always evaluate to false\\.$#" - count: 1 - path: src/Scope.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$includeKey\\)\\: Unexpected token \"\\$includeKey\", expected type at offset 18$#" - count: 2 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$includeObject\\)\\: Unexpected token \"\\$includeObject\", expected type at offset 18$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$includeObject\\)\\: Unexpected token \"\\$includeObject\", expected type at offset 73$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$includeObjects\\)\\: Unexpected token \"\\$includeObjects\", expected type at offset 18$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$key\\)\\: Unexpected token \"\\$key\", expected type at offset 102$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$linkedIds\\)\\: Unexpected token \"\\$linkedIds\", expected type at offset 48$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$relationship\\)\\: Unexpected token \"\\$relationship\", expected type at offset 47$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$relationships\\)\\: Unexpected token \"\\$relationships\", expected type at offset 44$#" - count: 2 - path: src/Serializer/JsonApiSerializer.php - - - - message: "#^PHPDoc tag @param has invalid value \\(\\$serializedData\\)\\: Unexpected token \"\\$serializedData\", expected type at offset 73$#" - count: 1 - path: src/Serializer/JsonApiSerializer.php - + path: src/Manager.php \ No newline at end of file diff --git a/psalm.baseline.xml b/psalm.baseline.xml index 48f5f885..c9e27b07 100644 --- a/psalm.baseline.xml +++ b/psalm.baseline.xml @@ -1,18 +1,5 @@ - - - int - - - $this->paginator->getQuery()->getMaxResults() - - - - - count - - getMeta diff --git a/psalm.xml b/psalm.xml index f03e94f8..e044bd6b 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ current; } /** * Get the last page. - * - * @return int */ - public function getLastPage() + public function getLastPage(): int { - return $this->total / $this->perPage; + return (int) floor($this->total / $this->perPage); } /** * Get the total. - * - * @return int */ - public function getTotal() + public function getTotal(): int { return $this->total; } /** * Get the count. - * - * @return int */ - public function getCount() + public function getCount(): int { return $this->itemCount; } /** * Get the number per page. - * - * @return int */ - public function getPerPage() + public function getPerPage(): int { return $this->perPage; } @@ -101,10 +90,8 @@ public function getPerPage() * Get the url for the given page. * * @param int $page - * - * @return string */ - public function getUrl($page) + public function getUrl($page): string { return ($this->urlFactory)($page); } diff --git a/src/Resource/MetaDataInterface.php b/src/Resource/MetaDataInterface.php index a9a2724a..9458f6ca 100644 --- a/src/Resource/MetaDataInterface.php +++ b/src/Resource/MetaDataInterface.php @@ -17,9 +17,8 @@ public function getMeta(); /** * Get the meta data. * - * @param string $metaKey - * - * @return array + * @return mixed */ - public function getMetaValue($metaKey); + #[\ReturnTypeWillChange] + public function getMetaValue(string $metaKey); } diff --git a/src/Serializer/Serializer.php b/src/Serializer/Serializer.php index cb3e07d9..434671e1 100644 --- a/src/Serializer/Serializer.php +++ b/src/Serializer/Serializer.php @@ -1,4 +1,5 @@ getManager()->getIncludeParams($scopeIdentifier); // Check if the method name actually exists - $methodName = 'include' . str_replace(' ', '', ucwords(str_replace('_', ' ', str_replace('-', ' ', $includeName)))); + $methodName = 'include' . str_replace( + ' ', + '', + ucwords( + str_replace( + '_', + ' ', + str_replace( + '-', + ' ', + $includeName + ) + ) + ) + ); $resource = $this->{$methodName}($data, $params); if ($resource === null) {