From da0ff8d8e8ac4172a395abc7fb8578170c23e699 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 16:54:55 -0600 Subject: [PATCH 1/7] feat: set minimum PHP version to 7.4 chore: add linting and fix for style guide --- composer.json | 23 +- composer.lock | 610 ++++++++++++++++++++++++++- src/Directives.php | 12 +- src/Directives/ExternalDirective.php | 8 +- src/Directives/KeyDirective.php | 12 +- src/Directives/ProvidesDirective.php | 12 +- src/Directives/RequiresDirective.php | 12 +- src/FederatedSchema.php | 108 +++-- src/Types/EntityObjectType.php | 31 +- src/Types/EntityRefObjectType.php | 3 - src/Utils/FederatedSchemaPrinter.php | 242 ++++++----- test/DirectivesTest.php | 20 +- test/EntitiesTest.php | 39 +- test/SchemaTest.php | 23 +- test/StarWarsData.php | 101 +++-- test/StarWarsSchema.php | 76 ++-- 16 files changed, 1015 insertions(+), 317 deletions(-) diff --git a/composer.json b/composer.json index b7f737f..2d959e4 100644 --- a/composer.json +++ b/composer.json @@ -4,17 +4,27 @@ "type": "library", "license": "MIT", "require": { - "php": "^7.1||^8.0", - "webonyx/graphql-php": "^0.13.8 || ^14.0" + "php": "^7.4 || ^8.0", + "webonyx/graphql-php": "^14.0" }, "scripts": { "test": "phpunit", "sync": "yarn install && composer update", - "commit": "yarn commit" + "commit": "yarn commit", + "dev:lint": [ + "@dev:lint:syntax", + "@dev:lint:style" + ], + "dev:lint:fix": "phpcbf", + "dev:lint:style": "phpcs --colors", + "dev:lint:syntax": "parallel-lint --colors src/ test/" }, "config": { "preferred-install": "dist", - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } }, "autoload": { "psr-4": { @@ -26,9 +36,14 @@ "Apollo\\Federation\\Tests\\": "test/" } }, + "minimum-stability": "dev", + "prefer-stable": true, "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3", "phpunit/phpunit": "^9.5", "psr/http-message": "^1.0", + "ramsey/coding-standard": "^2.0", "react/promise": "^2.7", "spatie/phpunit-snapshot-assertions": "^4.2" } diff --git a/composer.lock b/composer.lock index ff1b0f0..0c0835c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f8b96024627fdd75c89b99d46abcfd42", + "content-hash": "72a0742398b022e6b9ec978dcbcfea69", "packages": [ { "name": "webonyx/graphql-php", @@ -74,6 +74,81 @@ } ], "packages-dev": [ + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v0.7.2", + "source": { + "type": "git", + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "*", + "php-parallel-lint/php-parallel-lint": "^1.3.1", + "phpcompatibility/php-compatibility": "^9.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + }, + { + "name": "Contributors", + "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", + "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + }, + "time": "2022-02-04T12:51:07+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.1", @@ -370,6 +445,302 @@ }, "time": "2022-02-21T01:04:05+00:00" }, + { + "name": "php-parallel-lint/php-console-color", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-parallel-lint/PHP-Console-Color.git", + "reference": "7adfefd530aa2d7570ba87100a99e2483a543b88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-parallel-lint/PHP-Console-Color/zipball/7adfefd530aa2d7570ba87100a99e2483a543b88", + "reference": "7adfefd530aa2d7570ba87100a99e2483a543b88", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "jakub-onderka/php-console-color": "*" + }, + "require-dev": { + "php-parallel-lint/php-code-style": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.0", + "php-parallel-lint/php-var-dump-check": "0.*", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHP_Parallel_Lint\\PhpConsoleColor\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com" + } + ], + "description": "Simple library for creating colored console ouput.", + "support": { + "issues": "https://github.com/php-parallel-lint/PHP-Console-Color/issues", + "source": "https://github.com/php-parallel-lint/PHP-Console-Color/tree/v1.0.1" + }, + "time": "2021-12-25T06:49:29+00:00" + }, + { + "name": "php-parallel-lint/php-console-highlighter", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-parallel-lint/PHP-Console-Highlighter.git", + "reference": "5b4803384d3303cf8e84141039ef56c8a123138d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-parallel-lint/PHP-Console-Highlighter/zipball/5b4803384d3303cf8e84141039ef56c8a123138d", + "reference": "5b4803384d3303cf8e84141039ef56c8a123138d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.2", + "php-parallel-lint/php-console-color": "^1.0.1" + }, + "replace": { + "jakub-onderka/php-console-highlighter": "*" + }, + "require-dev": { + "php-parallel-lint/php-code-style": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.0", + "php-parallel-lint/php-var-dump-check": "0.*", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHP_Parallel_Lint\\PhpConsoleHighlighter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "acci@acci.cz", + "homepage": "http://www.acci.cz/" + } + ], + "description": "Highlight PHP code in terminal", + "support": { + "issues": "https://github.com/php-parallel-lint/PHP-Console-Highlighter/issues", + "source": "https://github.com/php-parallel-lint/PHP-Console-Highlighter/tree/v1.0.0" + }, + "time": "2022-02-18T08:23:19+00:00" + }, + { + "name": "php-parallel-lint/php-parallel-lint", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git", + "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6483c9832e71973ed29cf71bd6b3f4fde438a9de", + "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.3.0" + }, + "replace": { + "grogy/php-parallel-lint": "*", + "jakub-onderka/php-parallel-lint": "*" + }, + "require-dev": { + "nette/tester": "^1.3 || ^2.0", + "php-parallel-lint/php-console-highlighter": "0.* || ^1.0", + "squizlabs/php_codesniffer": "^3.6" + }, + "suggest": { + "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet" + }, + "bin": [ + "parallel-lint" + ], + "type": "library", + "autoload": { + "classmap": [ + "./src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Jakub Onderka", + "email": "ahoj@jakubonderka.cz" + } + ], + "description": "This tool check syntax of PHP files about 20x faster than serial check.", + "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint", + "support": { + "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues", + "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.3.2" + }, + "time": "2022-02-21T12:50:22+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.0.0-rc1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "e0e58d3d807b08aaa26b3df1a4a04a01a1d4ffa0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/e0e58d3d807b08aaa26b3df1a4a04a01a1d4ffa0", + "reference": "e0e58d3d807b08aaa26b3df1a4a04a01a1d4ffa0", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0 || dev-develop", + "squizlabs/php_codesniffer": "^3.7.1" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.5", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "time": "2022-12-07T08:22:01+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.0-alpha4", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "37c6da9a0aede973974ae02ef1af2dd641355e86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/37c6da9a0aede973974ae02ef1af2dd641355e86", + "reference": "37c6da9a0aede973974ae02ef1af2dd641355e86", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.7.1 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.3", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.3", + "yoast/phpunit-polyfills": "^1.0.1" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "time": "2022-10-25T13:57:45+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "2.2.0", @@ -597,6 +968,51 @@ }, "time": "2021-12-08T12:19:24+00:00" }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.15.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "6ff970a7101acfe99b3048e4bbfbc094e55c5b04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6ff970a7101acfe99b3048e4bbfbc094e55c5b04", + "reference": "6ff970a7101acfe99b3048e4bbfbc094e55c5b04", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.15.0" + }, + "time": "2022-12-07T16:12:39+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.15", @@ -1070,6 +1486,75 @@ }, "time": "2016-08-06T14:39:51+00:00" }, + { + "name": "ramsey/coding-standard", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/ramsey/coding-standard.git", + "reference": "4d2d6e9ee4b5dedba25a81d0961a6b8db11f9073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/coding-standard/zipball/4d2d6e9ee4b5dedba25a81d0961a6b8db11f9073", + "reference": "4d2d6e9ee4b5dedba25a81d0961a6b8db11f9073", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "php": "^7.4 || ^8", + "phpcsstandards/phpcsextra": "^1.0.0-alpha3", + "phpcsstandards/phpcsutils": "^1.0.0-alpha3", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5" + }, + "require-dev": { + "captainhook/captainhook": "^5.3", + "ergebnis/composer-normalize": "^2.6", + "ext-dom": "*", + "ramsey/conventional-commits": "^1.1", + "sebastian/diff": "^4.0" + }, + "bin": [ + "bin/xmllint", + "bin/xmllint-fix" + ], + "type": "phpcodesniffer-standard", + "extra": { + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A common coding standard for Ramsey's PHP libraries.", + "keywords": [ + "development", + "phpcs", + "standards", + "tools" + ], + "support": { + "issues": "https://github.com/ramsey/coding-standard/issues", + "source": "https://github.com/ramsey/coding-standard/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/ramsey", + "type": "github" + } + ], + "time": "2021-08-05T18:01:45+00:00" + }, { "name": "react/promise", "version": "v2.9.0", @@ -2110,6 +2595,67 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "slevomat/coding-standard", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/slevomat/coding-standard.git", + "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90", + "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "php": "^7.2 || ^8.0", + "phpstan/phpdoc-parser": "^1.5.1", + "squizlabs/php_codesniffer": "^3.6.2" + }, + "require-dev": { + "phing/phing": "2.17.3", + "php-parallel-lint/php-parallel-lint": "1.3.2", + "phpstan/phpstan": "1.4.10|1.7.1", + "phpstan/phpstan-deprecation-rules": "1.0.0", + "phpstan/phpstan-phpunit": "1.0.0|1.1.1", + "phpstan/phpstan-strict-rules": "1.2.3", + "phpunit/phpunit": "7.5.20|8.5.21|9.5.20" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "SlevomatCodingStandard\\": "SlevomatCodingStandard" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "support": { + "issues": "https://github.com/slevomat/coding-standard/issues", + "source": "https://github.com/slevomat/coding-standard/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/kukulich", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard", + "type": "tidelift" + } + ], + "time": "2022-05-25T10:58:12+00:00" + }, { "name": "spatie/phpunit-snapshot-assertions", "version": "4.2.14", @@ -2178,6 +2724,62 @@ ], "time": "2022-07-29T14:54:35+00:00" }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.7.1", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619", + "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2022-06-18T07:21:10+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v2.5.2", @@ -3204,12 +3806,12 @@ } ], "aliases": [], - "minimum-stability": "stable", + "minimum-stability": "dev", "stability-flags": [], - "prefer-stable": false, + "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^7.1||^8.0" + "php": "^7.4 || ^8.0" }, "platform-dev": [], "plugin-api-version": "2.3.0" diff --git a/src/Directives.php b/src/Directives.php index 8e71aa6..ce28618 100644 --- a/src/Directives.php +++ b/src/Directives.php @@ -4,8 +4,8 @@ namespace Apollo\Federation; -use Apollo\Federation\Directives\KeyDirective; use Apollo\Federation\Directives\ExternalDirective; +use Apollo\Federation\Directives\KeyDirective; use Apollo\Federation\Directives\ProvidesDirective; use Apollo\Federation\Directives\RequiresDirective; @@ -14,8 +14,10 @@ */ class Directives { - /** @var array */ - private static $directives; + /** + * @var array{key: KeyDirective, external: ExternalDirective, requires: RequiresDirective, provides: ProvidesDirective} + */ + private static array $directives; /** * Gets the @key directive @@ -51,6 +53,8 @@ public static function provides(): ProvidesDirective /** * Gets the directives that can be used on federated entity types + * + * @return array{key: KeyDirective, external: ExternalDirective, requires: RequiresDirective, provides: ProvidesDirective} */ public static function getDirectives(): array { @@ -59,7 +63,7 @@ public static function getDirectives(): array 'key' => new KeyDirective(), 'external' => new ExternalDirective(), 'requires' => new RequiresDirective(), - 'provides' => new ProvidesDirective() + 'provides' => new ProvidesDirective(), ]; } diff --git a/src/Directives/ExternalDirective.php b/src/Directives/ExternalDirective.php index afd7181..2da0029 100644 --- a/src/Directives/ExternalDirective.php +++ b/src/Directives/ExternalDirective.php @@ -1,11 +1,11 @@ 'external', - 'locations' => [DirectiveLocation::FIELD_DEFINITION] + 'locations' => [DirectiveLocation::FIELD_DEFINITION], ]); } } diff --git a/src/Directives/KeyDirective.php b/src/Directives/KeyDirective.php index 72469aa..07d9982 100644 --- a/src/Directives/KeyDirective.php +++ b/src/Directives/KeyDirective.php @@ -1,11 +1,13 @@ [ new FieldArgument([ 'name' => 'fields', - 'type' => Type::nonNull(Type::string()) - ]) - ] + 'type' => Type::nonNull(Type::string()), + ]), + ], ]); } } diff --git a/src/Directives/ProvidesDirective.php b/src/Directives/ProvidesDirective.php index f33b603..84c91aa 100644 --- a/src/Directives/ProvidesDirective.php +++ b/src/Directives/ProvidesDirective.php @@ -1,11 +1,13 @@ [ new FieldArgument([ 'name' => 'fields', - 'type' => Type::nonNull(Type::string()) - ]) - ] + 'type' => Type::nonNull(Type::string()), + ]), + ], ]); } } diff --git a/src/Directives/RequiresDirective.php b/src/Directives/RequiresDirective.php index 6583407..e0fafcb 100644 --- a/src/Directives/RequiresDirective.php +++ b/src/Directives/RequiresDirective.php @@ -1,11 +1,13 @@ [ new FieldArgument([ 'name' => 'fields', - 'type' => Type::nonNull(Type::string()) - ]) - ] + 'type' => Type::nonNull(Type::string()), + ]), + ], ]); } } diff --git a/src/FederatedSchema.php b/src/FederatedSchema.php index af3682b..3a070cc 100644 --- a/src/FederatedSchema.php +++ b/src/FederatedSchema.php @@ -4,17 +4,23 @@ namespace Apollo\Federation; -use GraphQL\Type\Schema; +use Apollo\Federation\Types\EntityObjectType; +use Apollo\Federation\Utils\FederatedSchemaPrinter; use GraphQL\Type\Definition\CustomScalarType; use GraphQL\Type\Definition\Directive; +use GraphQL\Type\Definition\ListOfType; use GraphQL\Type\Definition\ObjectType; -use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Definition\Type; +use GraphQL\Type\Definition\UnionType; +use GraphQL\Type\Schema; use GraphQL\Utils\TypeInfo; use GraphQL\Utils\Utils; -use Apollo\Federation\Types\EntityObjectType; -use Apollo\Federation\Utils\FederatedSchemaPrinter; +use function array_map; +use function array_merge; +use function array_values; +use function is_callable; +use function sprintf; /** * A federated GraphQL schema definition (see [related docs](https://www.apollographql.com/docs/apollo-server/federation/introduction)) @@ -56,12 +62,15 @@ class FederatedSchema extends Schema { /** @var EntityObjectType[] */ - protected $entityTypes; + protected array $entityTypes = []; /** @var Directive[] */ - protected $entityDirectives; + protected array $entityDirectives = []; - public function __construct($config) + /** + * @param array{query: Type, directives?: Directive[]} $config + */ + public function __construct(array $config) { $this->entityTypes = $this->extractEntityTypes($config); $this->entityDirectives = array_merge(Directives::getDirectives(), Directive::getInternalDirectives()); @@ -83,26 +92,30 @@ public function getEntityTypes(): array /** * Indicates whether the schema has entity types resolved - * - * @return bool */ public function hasEntityTypes(): bool { - return !empty($this->getEntityTypes()); + return $this->getEntityTypes() !== []; } /** + * @param array{directives?: Directive[]} $config + * * @return Directive[] */ private function getEntityDirectivesConfig(array $config): array { - $directives = isset($config['directives']) ? $config['directives'] : []; + $directives = $config['directives'] ?? []; $config['directives'] = array_merge($directives, $this->entityDirectives); return $config; } - /** @var array */ + /** + * @param array{query: Type} $config + * + * @return array{query: ObjectType} + */ private function getQueryTypeConfig(array $config): array { $queryTypeConfig = $config['query']->config; @@ -113,15 +126,17 @@ private function getQueryTypeConfig(array $config): array $queryTypeConfig['fields'] = array_merge( $queryTypeConfig['fields'], $this->getQueryTypeServiceFieldConfig(), - $this->getQueryTypeEntitiesFieldConfig($config) + $this->getQueryTypeEntitiesFieldConfig($config), ); return [ - 'query' => new ObjectType($queryTypeConfig) + 'query' => new ObjectType($queryTypeConfig), ]; } - /** @var array */ + /** + * @return array{_service: array{type: Type, resolve: callable}} + */ private function getQueryTypeServiceFieldConfig(): array { $serviceType = new ObjectType([ @@ -129,24 +144,24 @@ private function getQueryTypeServiceFieldConfig(): array 'fields' => [ 'sdl' => [ 'type' => Type::string(), - 'resolve' => function () { - return FederatedSchemaPrinter::doPrint($this); - } - ] - ] + 'resolve' => fn () => FederatedSchemaPrinter::doPrint($this), + ], + ], ]); return [ '_service' => [ 'type' => Type::nonNull($serviceType), - 'resolve' => function () { - return []; - } - ] + 'resolve' => fn () => [], + ], ]; } - /** @var array */ + /** + * @param array{resolve?: callable(mixed, mixed[], mixed, mixed):array} | null $config + * + * @return array{_entities: array{type: ListOfType, args: array{representations: array{type: Type}}, resolve: callable(mixed, mixed[], mixed, mixed):array}} + */ private function getQueryTypeEntitiesFieldConfig(?array $config): array { if (!$this->hasEntityTypes()) { @@ -155,14 +170,12 @@ private function getQueryTypeEntitiesFieldConfig(?array $config): array $entityType = new UnionType([ 'name' => '_Entity', - 'types' => array_values($this->getEntityTypes()) + 'types' => array_values($this->getEntityTypes()), ]); $anyType = new CustomScalarType([ 'name' => '_Any', - 'serialize' => function ($value) { - return $value; - } + 'serialize' => fn ($value) => $value, ]); return [ @@ -170,46 +183,57 @@ private function getQueryTypeEntitiesFieldConfig(?array $config): array 'type' => Type::listOf($entityType), 'args' => [ 'representations' => [ - 'type' => Type::nonNull(Type::listOf(Type::nonNull($anyType))) - ] + 'type' => Type::nonNull(Type::listOf(Type::nonNull($anyType))), + ], ], 'resolve' => function ($root, $args, $context, $info) use ($config) { if (isset($config) && isset($config['resolve']) && is_callable($config['resolve'])) { - return $config['resolve']($root, $args, $context, $info);; + return $config['resolve']($root, $args, $context, $info); } else { return $this->resolve($root, $args, $context, $info); } - } - ] + }, + ], ]; } - private function resolve($root, $args, $context, $info) + /** + * @param mixed $root + * @param mixed[] $args + * @param mixed $context + * @param mixed $info + * + * @return mixed[] + */ + private function resolve($root, array $args, $context, $info): array { return array_map(function ($ref) use ($context, $info) { Utils::invariant(isset($ref['__typename']), 'Type name must be provided in the reference.'); $typeName = $ref['__typename']; + + /** @var EntityObjectType $type */ $type = $info->schema->getType($typeName); Utils::invariant( - $type && $type instanceof EntityObjectType, + $type instanceof EntityObjectType, sprintf( - 'The _entities resolver tried to load an entity for type "%s", but no object type of that name was found in the schema', - $type->name - ) + 'The _entities resolver tried to load an entity for ' + . 'type "%s", but no object type of that name was found in the schema', + $type->name, + ), ); if (!$type->hasReferenceResolver()) { return $ref; } - $r = $type->resolveReference($ref, $context, $info); - return $r; + return $type->resolveReference($ref, $context, $info); }, $args['representations']); } + /** - * @param array $config + * @param array{query: Type} $config * * @return EntityObjectType[] */ diff --git a/src/Types/EntityObjectType.php b/src/Types/EntityObjectType.php index f2e70ad..b6b337b 100644 --- a/src/Types/EntityObjectType.php +++ b/src/Types/EntityObjectType.php @@ -4,10 +4,10 @@ namespace Apollo\Federation\Types; -use GraphQL\Utils\Utils; use GraphQL\Type\Definition\ObjectType; +use GraphQL\Utils\Utils; -use array_key_exists; +use function is_callable; /** * An entity is a type that can be referenced by another service. Entities create @@ -39,12 +39,11 @@ * ] * ] * ]); - * */ class EntityObjectType extends ObjectType { - /** @var array */ - private $keyFields; + /** @var string[] */ + private array $keyFields; /** @var callable */ public $referenceResolver; @@ -67,7 +66,7 @@ public function __construct(array $config) /** * Gets the fields that serve as the unique key or identifier of the entity. * - * @return array + * @return string[] */ public function getKeyFields(): array { @@ -76,8 +75,6 @@ public function getKeyFields(): array /** * Gets whether this entity has a resolver set - * - * @return bool */ public function hasReferenceResolver(): bool { @@ -90,28 +87,34 @@ public function hasReferenceResolver(): bool * @param mixed $ref * @param mixed $context * @param mixed $info + * + * @return mixed */ public function resolveReference($ref, $context = null, $info = null) { $this->validateReferenceResolver(); $this->validateReferenceKeys($ref); - $entity = ($this->referenceResolver)($ref, $context, $info); - - return $entity; + return ($this->referenceResolver)($ref, $context, $info); } - private function validateReferenceResolver() + private function validateReferenceResolver(): void { Utils::invariant(isset($this->referenceResolver), 'No reference resolver was set in the configuration.'); } - private function validateReferenceKeys($ref) + /** + * @param mixed[] $ref + */ + private function validateReferenceKeys(array $ref): void { Utils::invariant(isset($ref['__typename']), 'Type name must be provided in the reference.'); } - public static function validateResolveReference(array $config) + /** + * @param mixed[] $config + */ + public static function validateResolveReference(array $config): void { Utils::invariant(is_callable($config['__resolveReference']), 'Reference resolver has to be callable.'); } diff --git a/src/Types/EntityRefObjectType.php b/src/Types/EntityRefObjectType.php index a798ec4..2053bcd 100644 --- a/src/Types/EntityRefObjectType.php +++ b/src/Types/EntityRefObjectType.php @@ -12,9 +12,6 @@ */ class EntityRefObjectType extends EntityObjectType { - /** @var array */ - private $keyFields; - /** * @param mixed[] $config */ diff --git a/src/Utils/FederatedSchemaPrinter.php b/src/Utils/FederatedSchemaPrinter.php index 8eab202..bb5ceb5 100644 --- a/src/Utils/FederatedSchemaPrinter.php +++ b/src/Utils/FederatedSchemaPrinter.php @@ -1,7 +1,6 @@ !Directive::isSpecifiedDirective($type) + && !self::isFederatedDirective($type), + static fn (Type $type): bool => !Type::isBuiltInType($type), + $options, ); } - public static function isFederatedDirective($type): bool + public static function isFederatedDirective(Directive $type): bool { return in_array($type->name, ['key', 'provides', 'requires', 'external']); } /** - * @param bool[] $options + * @param callable(Directive):bool $directiveFilter + * @param callable(Type):bool | null $typeFilter + * @param OptionsType $options */ - private static function printFilteredSchema(Schema $schema, $directiveFilter, $typeFilter, $options): string - { - $directives = array_filter($schema->getDirectives(), static function ($directive) use ($directiveFilter) { - return $directiveFilter($directive); - }); + private static function printFilteredSchema( + Schema $schema, + callable $directiveFilter, + ?callable $typeFilter, + array $options + ): string { + $directives = array_filter( + $schema->getDirectives(), + static fn (Directive $directive): bool => $directiveFilter($directive), + ); $types = $schema->getTypeMap(); ksort($types); @@ -119,19 +126,24 @@ private static function printFilteredSchema(Schema $schema, $directiveFilter, $t "\n\n", array_filter( array_merge( - array_map(static function ($directive) use ($options) { - return self::printDirective($directive, $options); - }, $directives), - array_map(static function ($type) use ($options) { - return self::printType($type, $options); - }, $types) - ) - ) - ) + array_map( + static fn (Directive $directive): string => self::printDirective($directive, $options), + $directives, + ), + array_map( + static fn (Type $type): string => self::printType($type, $options), + $types, + ), + ), + ), + ), ); } - private static function printDirective($directive, $options): string + /** + * @param OptionsType $options + */ + private static function printDirective(Directive $directive, array $options): string { return self::printDescription($options, $directive) . 'directive @' . @@ -141,8 +153,16 @@ private static function printDirective($directive, $options): string implode(' | ', $directive->locations); } - private static function printDescription($options, $def, $indentation = '', $firstInBlock = true): string - { + /** + * @param OptionsType $options + * @param Directive | FieldArgument | InputObjectField $def + */ + private static function printDescription( + array $options, + object $def, + string $indentation = '', + bool $firstInBlock = true + ): string { if (!$def->description) { return ''; } @@ -222,8 +242,14 @@ private static function breakLine(string $line, int $maxLen): array return array_map('trim', $parts); } - private static function printDescriptionWithComments($lines, $indentation, $firstInBlock): string - { + /** + * @param string[] $lines + */ + private static function printDescriptionWithComments( + array $lines, + string $indentation, + bool $firstInBlock + ): string { $description = $indentation && !$firstInBlock ? "\n" : ''; foreach ($lines as $line) { @@ -237,12 +263,16 @@ private static function printDescriptionWithComments($lines, $indentation, $firs return $description; } - private static function escapeQuote($line): string + private static function escapeQuote(string $line): string { return str_replace('"""', '\\"""', $line); } - private static function printArgs($options, $args, $indentation = ''): string + /** + * @param OptionsType $options + * @param FieldArgument[] $args + */ + private static function printArgs(array $options, array $args, string $indentation = ''): string { if (!$args) { return ''; @@ -250,9 +280,10 @@ private static function printArgs($options, $args, $indentation = ''): string // If every arg does not have a description, print them on one line. if ( - Utils::every($args, static function ($arg) { - return empty($arg->description); - }) + Utils::every( + $args, + static fn (FieldArgument $arg): bool => !isset($arg->description) || $arg->description === '', + ) ) { return '(' . implode(', ', array_map('self::printInputValue', $args)) . ')'; } @@ -262,21 +293,24 @@ private static function printArgs($options, $args, $indentation = ''): string implode( "\n", array_map( - static function ($arg, $i) use ($indentation, $options) { - return self::printDescription($options, $arg, ' ' . $indentation, !$i) . - ' ' . - $indentation . - self::printInputValue($arg); - }, + static fn (FieldArgument $arg, int $i) => self::printDescription( + $options, + $arg, + ' ' . $indentation, + !$i, + ) . ' ' . $indentation . self::printInputValue($arg), $args, - array_keys($args) - ) + array_keys($args), + ), ), - $indentation + $indentation, ); } - private static function printInputValue($arg): string + /** + * @param InputObjectField | FieldArgument $arg + */ + private static function printInputValue(object $arg): string { $argDecl = $arg->name . ': ' . (string) $arg->getType(); @@ -288,7 +322,7 @@ private static function printInputValue($arg): string } /** - * @param bool[] $options + * @param OptionsType $options */ public static function printType(Type $type, array $options = []): string { @@ -300,7 +334,7 @@ public static function printType(Type $type, array $options = []): string } } - if ($type instanceof EntityObjectType || $type instanceof EntityRefObjectType) { + if ($type instanceof EntityObjectType) { return self::printEntityObject($type, $options); } @@ -336,7 +370,7 @@ public static function printType(Type $type, array $options = []): string } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printScalar(ScalarType $type, array $options): string { @@ -344,22 +378,20 @@ private static function printScalar(ScalarType $type, array $options): string } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printObject(ObjectType $type, array $options): string { - if (empty($type->getFields())) { + if ($type->getFields() === []) { return ''; } $interfaces = $type->getInterfaces(); - $implementedInterfaces = !empty($interfaces) + $implementedInterfaces = $interfaces !== [] ? ' implements ' . implode( ' & ', - array_map(static function ($i) { - return $i->name; - }, $interfaces) + array_map(static fn (InterfaceType $i): string => $i->name, $interfaces), ) : ''; @@ -371,23 +403,21 @@ private static function printObject(ObjectType $type, array $options): string $queryExtends, $type->name, $implementedInterfaces, - self::printFields($options, $type) + self::printFields($options, $type), ); } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printEntityObject(EntityObjectType $type, array $options): string { $interfaces = $type->getInterfaces(); - $implementedInterfaces = !empty($interfaces) + $implementedInterfaces = $interfaces !== [] ? ' implements ' . implode( ' & ', - array_map(static function ($i) { - return $i->name; - }, $interfaces) + array_map(static fn (InterfaceType $i): string => $i->name, $interfaces), ) : ''; @@ -407,28 +437,29 @@ private static function printEntityObject(EntityObjectType $type, array $options $type->name, $implementedInterfaces, $keyDirective, - self::printFields($options, $type) + self::printFields($options, $type), ); } /** - * @param bool[] $options + * @param OptionsType $options + * @param EntityObjectType | InterfaceType | ObjectType $type */ - private static function printFields($options, $type): string + private static function printFields(array $options, object $type): string { $fields = array_values($type->getFields()); if ($type->name === 'Query') { - $fields = array_filter($fields, function ($field) { - return $field->name !== '_service' && $field->name !== '_entities'; - }); + $fields = array_filter( + $fields, + fn (FieldDefinition $field): bool => $field->name !== '_service' && $field->name !== '_entities', + ); } return implode( "\n", array_map( - static function ($f, $i) use ($options) { - return self::printDescription($options, $f, ' ', !$i) . + static fn (FieldDefinition $f, int $i) => self::printDescription($options, $f, ' ', !$i) . ' ' . $f->name . self::printArgs($options, $f->args, ' ') . @@ -436,18 +467,20 @@ static function ($f, $i) use ($options) { (string) $f->getType() . self::printDeprecated($f) . ' ' . - self::printFieldFederatedDirectives($f); - }, + self::printFieldFederatedDirectives($f), $fields, - array_keys($fields) - ) + array_keys($fields), + ), ); } - private static function printDeprecated($fieldOrEnumVal): string + /** + * @param EnumValueDefinition | FieldDefinition $fieldOrEnumVal + */ + private static function printDeprecated(object $fieldOrEnumVal): string { - $reason = $fieldOrEnumVal->deprecationReason; - if (empty($reason)) { + $reason = $fieldOrEnumVal->deprecationReason ?? null; + if ($reason === null) { return ''; } if ($reason === '' || $reason === Directive::DEFAULT_DEPRECATION_REASON) { @@ -457,27 +490,27 @@ private static function printDeprecated($fieldOrEnumVal): string return ' @deprecated(reason: ' . Printer::doPrint(AST::astFromValue($reason, Type::string())) . ')'; } - private static function printFieldFederatedDirectives($field) + private static function printFieldFederatedDirectives(FieldDefinition $field): string { $directives = []; if (isset($field->config['isExternal']) && $field->config['isExternal'] === true) { - array_push($directives, '@external'); + $directives[] = '@external'; } if (isset($field->config['provides'])) { - array_push($directives, sprintf('@provides(fields: "%s")', $field->config['provides'])); + $directives[] = sprintf('@provides(fields: "%s")', $field->config['provides']); } if (isset($field->config['requires'])) { - array_push($directives, sprintf('@requires(fields: "%s")', $field->config['requires'])); + $directives[] = sprintf('@requires(fields: "%s")', $field->config['requires']); } return implode(' ', $directives); } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printInterface(InterfaceType $type, array $options): string { @@ -486,7 +519,7 @@ private static function printInterface(InterfaceType $type, array $options): str } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printUnion(UnionType $type, array $options): string { @@ -495,7 +528,7 @@ private static function printUnion(UnionType $type, array $options): string } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printEnum(EnumType $type, array $options): string { @@ -504,27 +537,26 @@ private static function printEnum(EnumType $type, array $options): string } /** - * @param bool[] $options + * @param EnumValueDefinition[] $values + * @param OptionsType $options */ - private static function printEnumValues($values, $options): string + private static function printEnumValues(array $values, array $options): string { return implode( "\n", array_map( - static function ($value, $i) use ($options) { - return self::printDescription($options, $value, ' ', !$i) . + static fn (EnumValueDefinition $value, int $i) => self::printDescription($options, $value, ' ', !$i) . ' ' . $value->name . - self::printDeprecated($value); - }, + self::printDeprecated($value), $values, - array_keys($values) - ) + array_keys($values), + ), ); } /** - * @param bool[] $options + * @param OptionsType $options */ private static function printInputObject(InputObjectType $type, array $options): string { @@ -537,20 +569,18 @@ private static function printInputObject(InputObjectType $type, array $options): implode( "\n", array_map( - static function ($f, $i) use ($options) { - return self::printDescription($options, $f, ' ', !$i) . ' ' . self::printInputValue($f); - }, + static fn (InputObjectField $f, int $i) => self::printDescription($options, $f, ' ', !$i) + . ' ' + . self::printInputValue($f), $fields, - array_keys($fields) - ) - ) + array_keys($fields), + ), + ), ); } /** - * @param bool[] $options - * - * @api + * @param OptionsType $options */ public static function printIntrospectionSchema(Schema $schema, array $options = []): string { @@ -558,7 +588,7 @@ public static function printIntrospectionSchema(Schema $schema, array $options = $schema, [Directive::class, 'isSpecifiedDirective'], [Introspection::class, 'isIntrospectionType'], - $options + $options, ); } } diff --git a/test/DirectivesTest.php b/test/DirectivesTest.php index 67c4871..a6c79ab 100644 --- a/test/DirectivesTest.php +++ b/test/DirectivesTest.php @@ -4,16 +4,14 @@ namespace Apollo\Federation\Tests; -use PHPUnit\Framework\TestCase; -use Spatie\Snapshots\MatchesSnapshots; - -use GraphQL\Type\Schema; -use GraphQL\Type\Definition\Type; -use GraphQL\Type\Definition\ObjectType; +use Apollo\Federation\Directives; use GraphQL\Language\DirectiveLocation; +use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\Type; +use GraphQL\Type\Schema; use GraphQL\Utils\SchemaPrinter; - -use Apollo\Federation\Directives; +use PHPUnit\Framework\TestCase; +use Spatie\Snapshots\MatchesSnapshots; class DirectivesTest extends TestCase { @@ -65,10 +63,10 @@ public function testItAddsDirectivesToSchema() 'query' => new ObjectType([ 'name' => 'Query', 'fields' => [ - '_' => ['type' => Type::string()] - ] + '_' => ['type' => Type::string()], + ], ]), - 'directives' => Directives::getDirectives() + 'directives' => Directives::getDirectives(), ]); $schemaSdl = SchemaPrinter::doPrint($schema); diff --git a/test/EntitiesTest.php b/test/EntitiesTest.php index de61813..215a33f 100644 --- a/test/EntitiesTest.php +++ b/test/EntitiesTest.php @@ -4,14 +4,11 @@ namespace Apollo\Federation\Tests; -use PHPUnit\Framework\TestCase; -use Spatie\Snapshots\MatchesSnapshots; - -use GraphQL\Type\Definition\Type; -use GraphQL\Error\InvariantViolation; - use Apollo\Federation\Types\EntityObjectType; use Apollo\Federation\Types\EntityRefObjectType; +use GraphQL\Type\Definition\Type; +use PHPUnit\Framework\TestCase; +use Spatie\Snapshots\MatchesSnapshots; class EntitiesTest extends TestCase { @@ -28,8 +25,8 @@ public function testCreatingEntityType() 'id' => ['type' => Type::int()], 'email' => ['type' => Type::string()], 'firstName' => ['type' => Type::string()], - 'lastName' => ['type' => Type::string()] - ] + 'lastName' => ['type' => Type::string()], + ], ]); $this->assertEqualsCanonicalizing($userType->getKeyFields(), $userTypeKeyFields); @@ -43,14 +40,12 @@ public function testCreatingEntityTypeWithCallable() $userType = new EntityObjectType([ 'name' => 'User', 'keyFields' => $userTypeKeyFields, - 'fields' => function () { - return [ - 'id' => ['type' => Type::int()], - 'email' => ['type' => Type::string()], - 'firstName' => ['type' => Type::string()], - 'lastName' => ['type' => Type::string()] - ]; - } + 'fields' => fn () => [ + 'id' => ['type' => Type::int()], + 'email' => ['type' => Type::string()], + 'firstName' => ['type' => Type::string()], + 'lastName' => ['type' => Type::string()], + ], ]); $this->assertEqualsCanonicalizing($userType->getKeyFields(), $userTypeKeyFields); @@ -64,7 +59,7 @@ public function testResolvingEntityReference() 'email' => 'luke@skywalker.com', 'firstName' => 'Luke', 'lastName' => 'Skywalker', - '__typename' => 'User' + '__typename' => 'User', ]; $userType = new EntityObjectType([ @@ -74,11 +69,9 @@ public function testResolvingEntityReference() 'id' => ['type' => Type::int()], 'email' => ['type' => Type::string()], 'firstName' => ['type' => Type::string()], - 'lastName' => ['type' => Type::string()] + 'lastName' => ['type' => Type::string()], ], - '__resolveReference' => function () use ($expectedRef) { - return $expectedRef; - } + '__resolveReference' => fn () => $expectedRef, ]); $actualRef = $userType->resolveReference(['id' => 1, 'email' => 'luke@skywalker.com', '__typename' => 'User']); @@ -95,8 +88,8 @@ public function testCreatingEntityRefType() 'keyFields' => $userTypeKeyFields, 'fields' => [ 'id' => ['type' => Type::int()], - 'email' => ['type' => Type::string()] - ] + 'email' => ['type' => Type::string()], + ], ]); $this->assertEqualsCanonicalizing($userType->getKeyFields(), $userTypeKeyFields); diff --git a/test/SchemaTest.php b/test/SchemaTest.php index f41fefc..7a8d765 100644 --- a/test/SchemaTest.php +++ b/test/SchemaTest.php @@ -4,15 +4,12 @@ namespace Apollo\Federation\Tests; -use PHPUnit\Framework\TestCase; -use Spatie\Snapshots\MatchesSnapshots; - use GraphQL\GraphQL; -use GraphQL\Type\Definition\Type; use GraphQL\Utils\SchemaPrinter; +use PHPUnit\Framework\TestCase; +use Spatie\Snapshots\MatchesSnapshots; -use Apollo\Federation\Tests\StarWarsSchema; -use Apollo\Federation\Tests\DungeonsAndDragonsSchema; +use function array_values; class SchemaTest extends TestCase { @@ -92,7 +89,6 @@ public function testResolvingEntityReferences() { $schema = StarWarsSchema::getEpisodesSchema(); - $query = ' query GetEpisodes($representations: [_Any!]!) { _entities(representations: $representations) { @@ -109,8 +105,8 @@ public function testResolvingEntityReferences() [ '__typename' => 'Episode', 'id' => 1, - ] - ] + ], + ], ]; $result = GraphQL::executeQuery($schema, $query, null, null, $variables); @@ -137,16 +133,15 @@ public function testOverrideSchemaResolver() 'representations' => [ [ '__typename' => 'Episode', - 'id' => 1 - ] - ] + 'id' => 1, + ], + ], ]; $result = GraphQL::executeQuery($schema, $query, null, null, $variables); // The custom resolver for this schema, always adds 1 to the id and gets the next // episode for the sake of testing the ability to change the resolver in the configuration - $this->assertEquals("The Empire Strikes Back", $result->data['_entities'][0]["title"]); + $this->assertEquals('The Empire Strikes Back', $result->data['_entities'][0]['title']); $this->assertMatchesSnapshot($result->toArray()); } } - \ No newline at end of file diff --git a/test/StarWarsData.php b/test/StarWarsData.php index c1cefa2..59ee2d0 100644 --- a/test/StarWarsData.php +++ b/test/StarWarsData.php @@ -4,102 +4,137 @@ namespace Apollo\Federation\Tests; +use function array_filter; +use function in_array; +use function reset; + +/** + * @psalm-type EpisodeType = array{id: int, title: string, characters: int[]} + * @psalm-type CharacterType = array{id: int, name: string, locations: int[]} + * @psalm-type LocationType = array{id: int, name: string} + */ class StarWarsData { - private static $episodes; + /** + * @var EpisodeType[] + */ + private static array $episodes; - private static $characters; + /** + * @var CharacterType[] + */ + private static array $characters; - private static $locations; + /** + * @var LocationType[] + */ + private static array $locations; - public static function getEpisodeById($id) + /** + * @return EpisodeType + */ + public static function getEpisodeById(int $id): array { - $matches = array_filter(self::getEpisodes(), function ($episode) use ($id) { - return $episode['id'] === $id; - }); + $matches = array_filter(self::getEpisodes(), fn ($episode) => $episode['id'] === $id); + return reset($matches); } - public static function getEpisodes() + /** + * @return EpisodeType[] + */ + public static function getEpisodes(): array { - if (!self::$episodes) { + if (!isset(self::$episodes)) { self::$episodes = [ [ 'id' => 1, 'title' => 'A New Hope', - 'characters' => [1, 2, 3] + 'characters' => [1, 2, 3], ], [ 'id' => 2, 'title' => 'The Empire Strikes Back', - 'characters' => [1, 2, 3] + 'characters' => [1, 2, 3], ], [ 'id' => 3, 'title' => 'Return of the Jedi', - 'characters' => [1, 2, 3] - ] + 'characters' => [1, 2, 3], + ], ]; } return self::$episodes; } - public static function getCharactersByIds($ids) + /** + * @param int[] $ids + * + * @return CharacterType[] + */ + public static function getCharactersByIds(array $ids): array { - return array_filter(self::getCharacters(), function ($character) use ($ids) { - return in_array($character['id'], $ids); - }); + return array_filter(self::getCharacters(), fn ($character) => in_array($character['id'], $ids)); } - public static function getCharacters() + /** + * @return CharacterType[] + */ + public static function getCharacters(): array { - if (!self::$characters) { + if (!isset(self::$characters)) { self::$characters = [ [ 'id' => 1, 'name' => 'Luke Skywalker', - 'locations' => [1, 2, 3] + 'locations' => [1, 2, 3], ], [ 'id' => 2, 'name' => 'Han Solo', - 'locations' => [1, 2] + 'locations' => [1, 2], ], [ 'id' => 3, 'name' => 'Leia Skywalker', - 'locations' => [3] - ] + 'locations' => [3], + ], ]; } return self::$characters; } - public static function getLocationsByIds($ids) + /** + * @param int[] $ids + * + * @return LocationType[] + */ + public static function getLocationsByIds(array $ids): array { - return array_filter(self::getLocations(), function ($location) use ($ids) { - return in_array($location['id'], $ids); - }); + return array_filter(self::getLocations(), fn ($location) => in_array($location['id'], $ids)); } - public static function getLocations() + /** + * @return LocationType[] + */ + public static function getLocations(): array { - if (!self::$locations) { + if (!isset(self::$locations)) { self::$locations = [ [ 'id' => 1, - 'name' => 'Tatooine' + 'name' => 'Tatooine', ], [ 'id' => 2, - 'name' => 'Endor' + 'name' => 'Endor', ], [ 'id' => 3, - 'name' => 'Hoth' - ] + 'name' => 'Hoth', + ], ]; } diff --git a/test/StarWarsSchema.php b/test/StarWarsSchema.php index 5594896..332f0c4 100644 --- a/test/StarWarsSchema.php +++ b/test/StarWarsSchema.php @@ -4,25 +4,27 @@ namespace Apollo\Federation\Tests; -use GraphQL\Type\Definition\Type; -use GraphQL\Type\Definition\ObjectType; - use Apollo\Federation\FederatedSchema; use Apollo\Federation\Types\EntityObjectType; use Apollo\Federation\Types\EntityRefObjectType; +use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\Type; + +use function array_map; class StarWarsSchema { - public static $episodesSchema; - public static $overRiddedEpisodesSchema; + public static FederatedSchema $episodesSchema; + public static FederatedSchema $overRiddedEpisodesSchema; public static function getEpisodesSchema(): FederatedSchema { if (!self::$episodesSchema) { self::$episodesSchema = new FederatedSchema([ - 'query' => self::getQueryType() + 'query' => self::getQueryType(), ]); } + return self::$episodesSchema; } @@ -31,16 +33,16 @@ public static function getEpisodesSchemaCustomResolver(): FederatedSchema if (!self::$overRiddedEpisodesSchema) { self::$overRiddedEpisodesSchema = new FederatedSchema([ 'query' => self::getQueryType(), - 'resolve' => function ($root, $args, $context, $info) { - return array_map(function ($ref) use ($info) { + 'resolve' => fn ($root, $args, $context, $info) => array_map(function ($ref) use ($info) { $typeName = $ref['__typename']; $type = $info->schema->getType($typeName); - $ref["id"] = $ref["id"] + 1; + $ref['id'] = $ref['id'] + 1; + return $type->resolveReference($ref); - }, $args['representations']); - } + }, $args['representations']), ]); } + return self::$overRiddedEpisodesSchema; } @@ -48,22 +50,19 @@ private static function getQueryType(): ObjectType { $episodeType = self::getEpisodeType(); - $queryType = new ObjectType([ + return new ObjectType([ 'name' => 'Query', 'fields' => [ 'episodes' => [ 'type' => Type::nonNull(Type::listOf(Type::nonNull($episodeType))), - 'resolve' => function () { - return StarWarsData::getEpisodes(); - } + 'resolve' => fn () => StarWarsData::getEpisodes(), ], 'deprecatedEpisodes' => [ 'type' => Type::nonNull(Type::listOf(Type::nonNull($episodeType))), - 'deprecationReason' => 'Because you should use the other one.' - ] - ] + 'deprecationReason' => 'Because you should use the other one.', + ], + ], ]); - return $queryType; } private static function getEpisodeType(): EntityObjectType @@ -73,26 +72,25 @@ private static function getEpisodeType(): EntityObjectType 'description' => 'A film in the Star Wars Trilogy', 'fields' => [ 'id' => [ - 'type' => Type::nonNull(Type::int()) + 'type' => Type::nonNull(Type::int()), ], 'title' => [ - 'type' => Type::nonNull(Type::string()) + 'type' => Type::nonNull(Type::string()), ], 'characters' => [ 'type' => Type::nonNull(Type::listOf(Type::nonNull(self::getCharacterType()))), - 'resolve' => function ($root) { - return StarWarsData::getCharactersByIds($root['characters']); - }, - 'provides' => 'name' - ] + 'resolve' => fn ($root) => StarWarsData::getCharactersByIds($root['characters']), + 'provides' => 'name', + ], ], 'keyFields' => ['id'], '__resolveReference' => function ($ref) { // print_r($ref); $entity = StarWarsData::getEpisodeById($ref['id']); - $entity["__typename"] = "Episode"; + $entity['__typename'] = 'Episode'; + return $entity; - } + }, ]); } @@ -104,21 +102,19 @@ private static function getCharacterType(): EntityRefObjectType 'fields' => [ 'id' => [ 'type' => Type::nonNull(Type::int()), - 'isExternal' => true + 'isExternal' => true, ], 'name' => [ 'type' => Type::nonNull(Type::string()), - 'isExternal' => true + 'isExternal' => true, ], 'locations' => [ 'type' => Type::nonNull(Type::listOf(self::getLocationType())), - 'resolve' => function ($root) { - return StarWarsData::getLocationsByIds($root['locations']); - }, - 'requires' => 'name' - ] + 'resolve' => fn ($root) => StarWarsData::getLocationsByIds($root['locations']), + 'requires' => 'name', + ], ], - 'keyFields' => ['id'] + 'keyFields' => ['id'], ]); } @@ -130,14 +126,14 @@ private static function getLocationType(): EntityRefObjectType 'fields' => [ 'id' => [ 'type' => Type::nonNull(Type::int()), - 'isExternal' => true + 'isExternal' => true, ], 'name' => [ 'type' => Type::nonNull(Type::string()), - 'isExternal' => true - ] + 'isExternal' => true, + ], ], - 'keyFields' => ['id'] + 'keyFields' => ['id'], ]); } } From 17759d09d84e0f996ff8493b07a71a3a19fa4cb1 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 19:10:28 -0600 Subject: [PATCH 2/7] chore: apply static analysis fixes from PHPStan and Psalm --- composer.json | 18 +- composer.lock | 1513 +++++++++++++++++++++++++- phpcs.xml.dist | 13 + phpstan.neon.dist | 6 + phpunit.xml | 29 - phpunit.xml.dist | 31 + psalm-baseline.xml | 2 + psalm.xml | 25 + src/Directives.php | 6 +- src/FederatedSchema.php | 115 +- src/Types/EntityObjectType.php | 31 +- src/Types/EntityRefObjectType.php | 2 +- src/Utils/FederatedSchemaPrinter.php | 50 +- test/DirectivesTest.php | 10 +- test/EntitiesTest.php | 8 +- test/SchemaTest.php | 31 +- test/StarWarsData.php | 6 +- test/StarWarsSchema.php | 4 +- 18 files changed, 1766 insertions(+), 134 deletions(-) create mode 100644 phpcs.xml.dist create mode 100644 phpstan.neon.dist delete mode 100644 phpunit.xml create mode 100644 phpunit.xml.dist create mode 100644 psalm-baseline.xml create mode 100644 psalm.xml diff --git a/composer.json b/composer.json index 2d959e4..1d8d195 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,20 @@ ], "dev:lint:fix": "phpcbf", "dev:lint:style": "phpcs --colors", - "dev:lint:syntax": "parallel-lint --colors src/ test/" + "dev:lint:syntax": "parallel-lint --colors src/ test/", + "dev:analyze": [ + "@dev:analyze:phpstan", + "@dev:analyze:psalm" + ], + "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G", + "dev:analyze:psalm": "psalm" }, "config": { "preferred-install": "dist", "sort-packages": true, "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true } }, "autoload": { @@ -41,10 +48,15 @@ "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.3", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-phpunit": "^1.3", "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.18.4", "psr/http-message": "^1.0", "ramsey/coding-standard": "^2.0", "react/promise": "^2.7", - "spatie/phpunit-snapshot-assertions": "^4.2" + "spatie/phpunit-snapshot-assertions": "^4.2", + "vimeo/psalm": "^5.2" } } diff --git a/composer.lock b/composer.lock index 0c0835c..5c27b97 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "72a0742398b022e6b9ec978dcbcfea69", + "content-hash": "48991876e2fb384436b394d2403eaf6e", "packages": [ { "name": "webonyx/graphql-php", @@ -74,6 +74,463 @@ } ], "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v0.7.2", @@ -149,6 +606,43 @@ }, "time": "2022-02-04T12:51:07+00:00" }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.1", @@ -219,6 +713,168 @@ ], "time": "2022-03-03T08:28:38+00:00" }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "666cb04a02f2801f3b19955fc23c824f9018bf64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/666cb04a02f2801f3b19955fc23c824f9018bf64", + "reference": "666cb04a02f2801f3b19955fc23c824f9018bf64", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26 || ^8.5.31", + "theofidry/php-cs-fixer-config": "^1.0", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/0.4.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-10T21:26:31+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.0", @@ -278,6 +934,57 @@ ], "time": "2022-03-03T13:19:32+00:00" }, + { + "name": "netresearch/jsonmapper", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + }, + "time": "2022-12-08T20:46:14+00:00" + }, { "name": "nikic/php-parser", "version": "v4.14.0", @@ -334,6 +1041,59 @@ }, "time": "2022-05-31T20:59:12+00:00" }, + { + "name": "openlss/lib-array2xml", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/nullivex/lib-array2xml.git", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "LSS": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bryan Tong", + "email": "bryan@nullivex.com", + "homepage": "https://www.nullivex.com" + }, + { + "name": "Tony Butler", + "email": "spudz76@gmail.com", + "homepage": "https://www.nullivex.com" + } + ], + "description": "Array2XML conversion library credit to lalit.org", + "homepage": "https://www.nullivex.com", + "keywords": [ + "array", + "array conversion", + "xml", + "xml conversion" + ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, + "time": "2019-03-29T20:06:56+00:00" + }, { "name": "phar-io/manifest", "version": "2.0.3", @@ -968,6 +1728,50 @@ }, "time": "2021-12-08T12:19:24+00:00" }, + { + "name": "phpstan/extension-installer", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/extension-installer.git", + "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0", + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.8.0" + }, + "require-dev": { + "composer/composer": "^2.0", + "php-parallel-lint/php-parallel-lint": "^1.2.0", + "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPStan\\ExtensionInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPStan\\ExtensionInstaller\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Composer plugin for automatic installation of PHPStan extensions", + "support": { + "issues": "https://github.com/phpstan/extension-installer/issues", + "source": "https://github.com/phpstan/extension-installer/tree/1.2.0" + }, + "time": "2022-10-17T12:59:16+00:00" + }, { "name": "phpstan/phpdoc-parser", "version": "1.15.0", @@ -1013,6 +1817,117 @@ }, "time": "2022-12-07T16:12:39+00:00" }, + { + "name": "phpstan/phpstan", + "version": "1.9.3", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "709999b91448d4f2bb07daffffedc889b33e461c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/709999b91448d4f2bb07daffffedc889b33e461c", + "reference": "709999b91448d4f2bb07daffffedc889b33e461c", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.9.3" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-12-13T10:28:10+00:00" + }, + { + "name": "phpstan/phpstan-phpunit", + "version": "1.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan-phpunit.git", + "reference": "cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088", + "reference": "cd9c6938f8bbfcb6da3ed5a3c7ea60873825d088", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpstan/phpstan": "^1.9.3" + }, + "conflict": { + "phpunit/phpunit": "<7.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon", + "rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "PHPStan\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPUnit extensions and rules for PHPStan", + "support": { + "issues": "https://github.com/phpstan/phpstan-phpunit/issues", + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.2" + }, + "time": "2022-12-13T15:08:22+00:00" + }, { "name": "phpunit/php-code-coverage", "version": "9.2.15", @@ -1433,6 +2348,114 @@ ], "time": "2022-06-19T12:14:25+00:00" }, + { + "name": "psalm/plugin-phpunit", + "version": "0.18.4", + "source": { + "type": "git", + "url": "https://github.com/psalm/psalm-plugin-phpunit.git", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "reference": "e4ab3096653d9eb6f6d0ea5f4461898d59ae4dbc", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.10", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "ext-simplexml": "*", + "php": "^7.1 || ^8.0", + "vimeo/psalm": "dev-master || dev-4.x || ^4.7.1 || ^5@beta || ^5.0" + }, + "conflict": { + "phpunit/phpunit": "<7.5" + }, + "require-dev": { + "codeception/codeception": "^4.0.3", + "php": "^7.3 || ^8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.3.1", + "weirdan/codeception-psalm-module": "^0.11.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "type": "psalm-plugin", + "extra": { + "psalm": { + "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin" + } + }, + "autoload": { + "psr-4": { + "Psalm\\PhpUnitPlugin\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Brown", + "email": "github@muglug.com" + } + ], + "description": "Psalm plugin for PHPUnit", + "support": { + "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues", + "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.18.4" + }, + "time": "2022-12-03T07:47:07+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -1486,6 +2509,56 @@ }, "time": "2016-08-06T14:39:51+00:00" }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, { "name": "ramsey/coding-standard", "version": "2.0.3", @@ -2774,11 +3847,110 @@ "standards" ], "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2022-06-18T07:21:10+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.16", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", + "reference": "8e9b9c8dfb33af6057c94e1b44846bee700dc5ef", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.16" }, - "time": "2022-06-18T07:21:10+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-25T14:09:27+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2847,6 +4019,70 @@ ], "time": "2022-01-02T09:53:40+00:00" }, + { + "name": "symfony/filesystem", + "version": "v5.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-09-21T19:53:16+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.26.0", @@ -3177,6 +4413,85 @@ ], "time": "2022-05-24T11:49:31+00:00" }, + { + "name": "symfony/polyfill-php73", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, { "name": "symfony/polyfill-php80", "version": "v1.26.0", @@ -3535,6 +4850,89 @@ ], "time": "2022-07-28T13:33:28+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-30T19:17:29+00:00" + }, { "name": "symfony/string", "version": "v5.4.11", @@ -3746,6 +5144,111 @@ ], "time": "2021-07-28T10:34:58+00:00" }, + { + "name": "vimeo/psalm", + "version": "5.2.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/fb685a16df3050d4c18d8a4100fe83abe6458cba", + "reference": "fb685a16df3050d4c18d8a4100fe83abe6458cba", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer/package-versions-deprecated": "^1.10.0", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", + "fidry/cpu-core-counter": "^0.4.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.13", + "openlss/lib-array2xml": "^1.0", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0", + "sebastian/diff": "^4.0", + "symfony/console": "^4.1.6 || ^5.0 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/polyfill-php80": "^1.25" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "brianium/paratest": "^6.0", + "ext-curl": "*", + "mockery/mockery": "^1.5", + "nunomaduro/mock-final-classes": "^1.1", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/phpdoc-parser": "^1.6", + "phpunit/phpunit": "^9.5", + "psalm/plugin-mockery": "^1.1", + "psalm/plugin-phpunit": "^0.18", + "slevomat/coding-standard": "^8.4", + "squizlabs/php_codesniffer": "^3.6", + "symfony/process": "^4.4 || ^5.0 || ^6.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev", + "dev-4.x": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php" + ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/5.2.0" + }, + "time": "2022-12-12T08:18:56+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..435bba6 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,13 @@ + + + + + + + + ./src + ./test + + + + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..d03b93f --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,6 @@ +parameters: + level: max + treatPhpDocTypesAsCertain: false + paths: + - ./src + - ./test diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 75731ed..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - ./src - - - ./src/Utils/FederatedSchemaPrinter.php - - - - - - - - - - - - ./test/ - - - - - ReactPromise - - - - diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..1bc7cb9 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./test + + + + + + ./src + + + ./src/Utils/FederatedSchemaPrinter.php + + + + + + + + + + + + diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..ca62028 --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,2 @@ + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..724dac2 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Directives.php b/src/Directives.php index ce28618..50751a2 100644 --- a/src/Directives.php +++ b/src/Directives.php @@ -15,9 +15,9 @@ class Directives { /** - * @var array{key: KeyDirective, external: ExternalDirective, requires: RequiresDirective, provides: ProvidesDirective} + * @var array{key: KeyDirective, external: ExternalDirective, requires: RequiresDirective, provides: ProvidesDirective} | null */ - private static array $directives; + private static ?array $directives = null; /** * Gets the @key directive @@ -58,7 +58,7 @@ public static function provides(): ProvidesDirective */ public static function getDirectives(): array { - if (!self::$directives) { + if (self::$directives === null) { self::$directives = [ 'key' => new KeyDirective(), 'external' => new ExternalDirective(), diff --git a/src/FederatedSchema.php b/src/FederatedSchema.php index 3a070cc..471c1a1 100644 --- a/src/FederatedSchema.php +++ b/src/FederatedSchema.php @@ -10,6 +10,7 @@ use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\ListOfType; use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\UnionType; use GraphQL\Type\Schema; @@ -68,7 +69,7 @@ class FederatedSchema extends Schema protected array $entityDirectives = []; /** - * @param array{query: Type, directives?: Directive[]} $config + * @param array{query: Type, directives?: Directive[], resolve?: callable} $config */ public function __construct(array $config) { @@ -99,9 +100,9 @@ public function hasEntityTypes(): bool } /** - * @param array{directives?: Directive[]} $config + * @param array{query: Type, directives?: Directive[], resolve?: callable} $config * - * @return Directive[] + * @return array{query: Type, directives: Directive[], resolve?: callable} */ private function getEntityDirectivesConfig(array $config): array { @@ -112,19 +113,23 @@ private function getEntityDirectivesConfig(array $config): array } /** - * @param array{query: Type} $config + * @param array{query: Type, directives?: Directive[], resolve?: callable} $config * * @return array{query: ObjectType} */ private function getQueryTypeConfig(array $config): array { + /** @var array{fields: callable():array | array} $queryTypeConfig */ $queryTypeConfig = $config['query']->config; + if (is_callable($queryTypeConfig['fields'])) { - $queryTypeConfig['fields'] = $queryTypeConfig['fields'](); + $fields = $queryTypeConfig['fields'](); + } else { + $fields = $queryTypeConfig['fields']; } $queryTypeConfig['fields'] = array_merge( - $queryTypeConfig['fields'], + $fields, $this->getQueryTypeServiceFieldConfig(), $this->getQueryTypeEntitiesFieldConfig($config), ); @@ -158,11 +163,11 @@ private function getQueryTypeServiceFieldConfig(): array } /** - * @param array{resolve?: callable(mixed, mixed[], mixed, mixed):array} | null $config + * @param array{query: Type, directives?: Directive[], resolve?: mixed} $config * - * @return array{_entities: array{type: ListOfType, args: array{representations: array{type: Type}}, resolve: callable(mixed, mixed[], mixed, mixed):array}} + * @return array{_entities?: array{type: ListOfType, args: array{representations: array{type: Type}}, resolve: callable}} */ - private function getQueryTypeEntitiesFieldConfig(?array $config): array + private function getQueryTypeEntitiesFieldConfig(array $config): array { if (!$this->hasEntityTypes()) { return []; @@ -175,7 +180,13 @@ private function getQueryTypeEntitiesFieldConfig(?array $config): array $anyType = new CustomScalarType([ 'name' => '_Any', - 'serialize' => fn ($value) => $value, + 'serialize' => + /** + * @param scalar $value + * + * @return scalar + */ + fn ($value) => $value, ]); return [ @@ -186,60 +197,84 @@ private function getQueryTypeEntitiesFieldConfig(?array $config): array 'type' => Type::nonNull(Type::listOf(Type::nonNull($anyType))), ], ], - 'resolve' => function ($root, $args, $context, $info) use ($config) { - if (isset($config) && isset($config['resolve']) && is_callable($config['resolve'])) { - return $config['resolve']($root, $args, $context, $info); - } else { - return $this->resolve($root, $args, $context, $info); - } - }, + 'resolve' => + /** + * @param mixed $root + * @param array{representations: array} $args + * @param mixed $context + * + * @return mixed[] + */ + function ($root, array $args, $context, ResolveInfo $info) use ($config): array { + if (isset($config['resolve']) && is_callable($config['resolve'])) { + /** @var mixed[] */ + return $config['resolve']($root, $args, $context, $info); + } else { + /** + * PHPStan is weird... + * + * @var array{representations: array} $argsArgument + * @psalm-suppress UnnecessaryVarAnnotation + */ + $argsArgument = $args; + + return $this->resolve($root, $argsArgument, $context, $info); + } + }, ], ]; } /** * @param mixed $root - * @param mixed[] $args + * @param array{representations: array} $args * @param mixed $context - * @param mixed $info * * @return mixed[] + * + * @psalm-suppress UnusedParam $root is unused here, but it might be used by another callable */ - private function resolve($root, array $args, $context, $info): array + private function resolve($root, array $args, $context, ResolveInfo $info): array { - return array_map(function ($ref) use ($context, $info) { - Utils::invariant(isset($ref['__typename']), 'Type name must be provided in the reference.'); + return array_map( + /** + * @param array{__typename?: string} $ref + */ + function (array $ref) use ($context, $info) { + Utils::invariant(isset($ref['__typename']), 'Type name must be provided in the reference.'); - $typeName = $ref['__typename']; + $typeName = $ref['__typename'] ?? ''; - /** @var EntityObjectType $type */ - $type = $info->schema->getType($typeName); + /** @var EntityObjectType $type */ + $type = $info->schema->getType($typeName); - Utils::invariant( - $type instanceof EntityObjectType, - sprintf( - 'The _entities resolver tried to load an entity for ' - . 'type "%s", but no object type of that name was found in the schema', - $type->name, - ), - ); + Utils::invariant( + $type instanceof EntityObjectType, + sprintf( + 'The _entities resolver tried to load an entity for ' + . 'type "%s", but no object type of that name was found in the schema', + $type->name, + ), + ); - if (!$type->hasReferenceResolver()) { - return $ref; - } + if (!$type->hasReferenceResolver()) { + return $ref; + } - return $type->resolveReference($ref, $context, $info); - }, $args['representations']); + return $type->resolveReference($ref, $context, $info); + }, + $args['representations'], + ); } /** - * @param array{query: Type} $config + * @param array{query: Type, directives?: Directive[], resolve?: callable} $config * * @return EntityObjectType[] */ private function extractEntityTypes(array $config): array { - $resolvedTypes = TypeInfo::extractTypes($config['query']); + $resolvedTypes = TypeInfo::extractTypes($config['query']) ?? []; $entityTypes = []; foreach ($resolvedTypes as $type) { diff --git a/src/Types/EntityObjectType.php b/src/Types/EntityObjectType.php index b6b337b..b4c4f30 100644 --- a/src/Types/EntityObjectType.php +++ b/src/Types/EntityObjectType.php @@ -45,19 +45,19 @@ class EntityObjectType extends ObjectType /** @var string[] */ private array $keyFields; - /** @var callable */ - public $referenceResolver; + /** @var callable | null */ + public $referenceResolver = null; /** - * @param mixed[] $config + * @param array{keyFields?: string[], __resolveReference?: callable} $config */ public function __construct(array $config) { - $this->keyFields = $config['keyFields']; + $this->keyFields = $config['keyFields'] ?? []; if (isset($config['__resolveReference'])) { self::validateResolveReference($config); - $this->referenceResolver = $config['__resolveReference']; + $this->referenceResolver = $config['__resolveReference'] ?? null; } parent::__construct($config); @@ -78,44 +78,47 @@ public function getKeyFields(): array */ public function hasReferenceResolver(): bool { - return isset($this->referenceResolver); + return $this->referenceResolver !== null; } /** * Resolves an entity from a reference * - * @param mixed $ref + * @param array{__typename?: mixed} | null $ref * @param mixed $context * @param mixed $info * * @return mixed */ - public function resolveReference($ref, $context = null, $info = null) + public function resolveReference(?array $ref = null, $context = null, $info = null) { $this->validateReferenceResolver(); $this->validateReferenceKeys($ref); - return ($this->referenceResolver)($ref, $context, $info); + /** @var callable $resolver */ + $resolver = $this->referenceResolver; + + return ($resolver)($ref, $context, $info); } private function validateReferenceResolver(): void { - Utils::invariant(isset($this->referenceResolver), 'No reference resolver was set in the configuration.'); + Utils::invariant($this->hasReferenceResolver(), 'No reference resolver was set in the configuration.'); } /** - * @param mixed[] $ref + * @param array{__typename?: mixed} | null $ref */ - private function validateReferenceKeys(array $ref): void + private function validateReferenceKeys(?array $ref): void { Utils::invariant(isset($ref['__typename']), 'Type name must be provided in the reference.'); } /** - * @param mixed[] $config + * @param array{keyFields?: string[], __resolveReference?: callable} $config */ public static function validateResolveReference(array $config): void { - Utils::invariant(is_callable($config['__resolveReference']), 'Reference resolver has to be callable.'); + Utils::invariant(is_callable($config['__resolveReference'] ?? ''), 'Reference resolver has to be callable.'); } } diff --git a/src/Types/EntityRefObjectType.php b/src/Types/EntityRefObjectType.php index 2053bcd..cba3e2d 100644 --- a/src/Types/EntityRefObjectType.php +++ b/src/Types/EntityRefObjectType.php @@ -13,7 +13,7 @@ class EntityRefObjectType extends EntityObjectType { /** - * @param mixed[] $config + * @param array{keyFields?: string[], __resolveReference?: callable} $config */ public function __construct(array $config) { diff --git a/src/Utils/FederatedSchemaPrinter.php b/src/Utils/FederatedSchemaPrinter.php index bb5ceb5..21de22a 100644 --- a/src/Utils/FederatedSchemaPrinter.php +++ b/src/Utils/FederatedSchemaPrinter.php @@ -34,6 +34,7 @@ use Apollo\Federation\Types\EntityObjectType; use Apollo\Federation\Types\EntityRefObjectType; use GraphQL\Error\Error; +use GraphQL\Language\AST\Node; use GraphQL\Language\Printer; use GraphQL\Type\Definition\Directive; use GraphQL\Type\Definition\EnumType; @@ -42,6 +43,7 @@ use GraphQL\Type\Definition\FieldDefinition; use GraphQL\Type\Definition\InputObjectField; use GraphQL\Type\Definition\InputObjectType; +use GraphQL\Type\Definition\InputType; use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\ScalarType; @@ -61,6 +63,7 @@ use function explode; use function implode; use function in_array; +use function is_string; use function ksort; use function mb_strlen; use function preg_match_all; @@ -102,13 +105,13 @@ public static function isFederatedDirective(Directive $type): bool /** * @param callable(Directive):bool $directiveFilter - * @param callable(Type):bool | null $typeFilter + * @param callable(Type):bool $typeFilter * @param OptionsType $options */ private static function printFilteredSchema( Schema $schema, callable $directiveFilter, - ?callable $typeFilter, + callable $typeFilter, array $options ): string { $directives = array_filter( @@ -155,7 +158,6 @@ private static function printDirective(Directive $directive, array $options): st /** * @param OptionsType $options - * @param Directive | FieldArgument | InputObjectField $def */ private static function printDescription( array $options, @@ -163,7 +165,7 @@ private static function printDescription( string $indentation = '', bool $firstInBlock = true ): string { - if (!$def->description) { + if (!isset($def->description) || !is_string($def->description)) { return ''; } @@ -181,7 +183,7 @@ private static function printDescription( } // Format a multi-line block quote to account for leading space. - $hasLeadingSpace = isset($lines[0]) && (substr($lines[0], 0, 1) === ' ' || substr($lines[0], 0, 1) === '\t'); + $hasLeadingSpace = isset($lines[0]) && (substr($lines[0], 0, 1) === ' ' || substr($lines[0], 0, 1) === "\t"); if (!$hasLeadingSpace) { $description .= "\n"; @@ -285,9 +287,12 @@ private static function printArgs(array $options, array $args, string $indentati static fn (FieldArgument $arg): bool => !isset($arg->description) || $arg->description === '', ) ) { - return '(' . implode(', ', array_map('self::printInputValue', $args)) . ')'; + return '(' . implode(', ', array_map([static::class, 'printInputValue'], $args)) . ')'; } + /** @var int[] $argIndexes */ + $argIndexes = array_keys($args); + return sprintf( "(\n%s\n%s)", implode( @@ -300,7 +305,7 @@ private static function printArgs(array $options, array $args, string $indentati !$i, ) . ' ' . $indentation . self::printInputValue($arg), $args, - array_keys($args), + $argIndexes, ), ), $indentation, @@ -315,7 +320,13 @@ private static function printInputValue(object $arg): string $argDecl = $arg->name . ': ' . (string) $arg->getType(); if ($arg->defaultValueExists()) { - $argDecl .= ' = ' . Printer::doPrint(AST::astFromValue($arg->defaultValue, $arg->getType())); + /** @var InputType $inputType */ + $inputType = $arg->getType(); + + /** @var Node $astNode */ + $astNode = AST::astFromValue($arg->defaultValue, $inputType); + + $argDecl .= ' = ' . Printer::doPrint($astNode); } return $argDecl; @@ -487,23 +498,29 @@ private static function printDeprecated(object $fieldOrEnumVal): string return ' @deprecated'; } - return ' @deprecated(reason: ' . Printer::doPrint(AST::astFromValue($reason, Type::string())) . ')'; + /** @var Node $astNode */ + $astNode = AST::astFromValue($reason, Type::string()); + + return ' @deprecated(reason: ' . Printer::doPrint($astNode) . ')'; } private static function printFieldFederatedDirectives(FieldDefinition $field): string { $directives = []; - if (isset($field->config['isExternal']) && $field->config['isExternal'] === true) { + /** @var array{isExternal?: bool, provides?: string, requires?: string} $config */ + $config = $field->config; + + if (isset($config['isExternal']) && $config['isExternal'] === true) { $directives[] = '@external'; } - if (isset($field->config['provides'])) { - $directives[] = sprintf('@provides(fields: "%s")', $field->config['provides']); + if (isset($config['provides'])) { + $directives[] = sprintf('@provides(fields: "%s")', $config['provides']); } - if (isset($field->config['requires'])) { - $directives[] = sprintf('@requires(fields: "%s")', $field->config['requires']); + if (isset($config['requires'])) { + $directives[] = sprintf('@requires(fields: "%s")', $config['requires']); } return implode(' ', $directives); @@ -542,6 +559,9 @@ private static function printEnum(EnumType $type, array $options): string */ private static function printEnumValues(array $values, array $options): string { + /** @var int[] $valueIndexes */ + $valueIndexes = array_keys($values); + return implode( "\n", array_map( @@ -550,7 +570,7 @@ private static function printEnumValues(array $values, array $options): string $value->name . self::printDeprecated($value), $values, - array_keys($values), + $valueIndexes, ), ); } diff --git a/test/DirectivesTest.php b/test/DirectivesTest.php index a6c79ab..7023c6f 100644 --- a/test/DirectivesTest.php +++ b/test/DirectivesTest.php @@ -17,7 +17,7 @@ class DirectivesTest extends TestCase { use MatchesSnapshots; - public function testKeyDirective() + public function testKeyDirective(): void { $config = Directives::key()->config; @@ -27,7 +27,7 @@ public function testKeyDirective() $this->assertEqualsCanonicalizing($config['locations'], $expectedLocations); } - public function testExternalDirective() + public function testExternalDirective(): void { $config = Directives::external()->config; @@ -37,7 +37,7 @@ public function testExternalDirective() $this->assertEqualsCanonicalizing($config['locations'], $expectedLocations); } - public function testRequiresDirective() + public function testRequiresDirective(): void { $config = Directives::requires()->config; @@ -47,7 +47,7 @@ public function testRequiresDirective() $this->assertEqualsCanonicalizing($config['locations'], $expectedLocations); } - public function testProvidesDirective() + public function testProvidesDirective(): void { $config = Directives::provides()->config; @@ -57,7 +57,7 @@ public function testProvidesDirective() $this->assertEqualsCanonicalizing($config['locations'], $expectedLocations); } - public function testItAddsDirectivesToSchema() + public function testItAddsDirectivesToSchema(): void { $schema = new Schema([ 'query' => new ObjectType([ diff --git a/test/EntitiesTest.php b/test/EntitiesTest.php index 215a33f..471b76f 100644 --- a/test/EntitiesTest.php +++ b/test/EntitiesTest.php @@ -14,7 +14,7 @@ class EntitiesTest extends TestCase { use MatchesSnapshots; - public function testCreatingEntityType() + public function testCreatingEntityType(): void { $userTypeKeyFields = ['id', 'email']; @@ -33,7 +33,7 @@ public function testCreatingEntityType() $this->assertMatchesSnapshot($userType->config); } - public function testCreatingEntityTypeWithCallable() + public function testCreatingEntityTypeWithCallable(): void { $userTypeKeyFields = ['id', 'email']; @@ -52,7 +52,7 @@ public function testCreatingEntityTypeWithCallable() $this->assertMatchesSnapshot($userType->config); } - public function testResolvingEntityReference() + public function testResolvingEntityReference(): void { $expectedRef = [ 'id' => 1, @@ -79,7 +79,7 @@ public function testResolvingEntityReference() $this->assertEquals($expectedRef, $actualRef); } - public function testCreatingEntityRefType() + public function testCreatingEntityRefType(): void { $userTypeKeyFields = ['id', 'email']; diff --git a/test/SchemaTest.php b/test/SchemaTest.php index 7a8d765..227c7fe 100644 --- a/test/SchemaTest.php +++ b/test/SchemaTest.php @@ -5,6 +5,7 @@ namespace Apollo\Federation\Tests; use GraphQL\GraphQL; +use GraphQL\Type\Definition\UnionType; use GraphQL\Utils\SchemaPrinter; use PHPUnit\Framework\TestCase; use Spatie\Snapshots\MatchesSnapshots; @@ -15,7 +16,7 @@ class SchemaTest extends TestCase { use MatchesSnapshots; - public function testRunningQueries() + public function testRunningQueries(): void { $schema = StarWarsSchema::getEpisodesSchema(); $query = 'query GetEpisodes { episodes { id title characters { id name } } }'; @@ -25,7 +26,7 @@ public function testRunningQueries() $this->assertMatchesSnapshot($result->toArray()); } - public function testEntityTypes() + public function testEntityTypes(): void { $schema = StarWarsSchema::getEpisodesSchema(); @@ -38,11 +39,13 @@ public function testEntityTypes() $this->assertArrayHasKey('Location', $entityTypes); } - public function testMetaTypes() + public function testMetaTypes(): void { $schema = StarWarsSchema::getEpisodesSchema(); $anyType = $schema->getType('_Any'); + + /** @var UnionType $entitiesType */ $entitiesType = $schema->getType('_Entity'); $this->assertNotNull($anyType); @@ -50,7 +53,7 @@ public function testMetaTypes() $this->assertEqualsCanonicalizing($entitiesType->getTypes(), array_values($schema->getEntityTypes())); } - public function testDirectives() + public function testDirectives(): void { $schema = StarWarsSchema::getEpisodesSchema(); $directives = $schema->getDirectives(); @@ -67,7 +70,7 @@ public function testDirectives() $this->assertArrayHasKey('deprecated', $directives); } - public function testServiceSdl() + public function testServiceSdl(): void { $schema = StarWarsSchema::getEpisodesSchema(); $query = 'query GetServiceSdl { _service { sdl } }'; @@ -77,7 +80,7 @@ public function testServiceSdl() $this->assertMatchesSnapshot($result->toArray()); } - public function testSchemaSdl() + public function testSchemaSdl(): void { $schema = StarWarsSchema::getEpisodesSchema(); $schemaSdl = SchemaPrinter::doPrint($schema); @@ -85,7 +88,7 @@ public function testSchemaSdl() $this->assertMatchesSnapshot($schemaSdl); } - public function testResolvingEntityReferences() + public function testResolvingEntityReferences(): void { $schema = StarWarsSchema::getEpisodesSchema(); @@ -110,11 +113,15 @@ public function testResolvingEntityReferences() ]; $result = GraphQL::executeQuery($schema, $query, null, null, $variables); - $this->assertCount(1, $result->data['_entities']); + + /** @var mixed[] $entities */ + $entities = $result->data['_entities'] ?? []; + + $this->assertCount(1, $entities); $this->assertMatchesSnapshot($result->toArray()); } - public function testOverrideSchemaResolver() + public function testOverrideSchemaResolver(): void { $schema = StarWarsSchema::getEpisodesSchemaCustomResolver(); @@ -139,9 +146,13 @@ public function testOverrideSchemaResolver() ]; $result = GraphQL::executeQuery($schema, $query, null, null, $variables); + + /** @var array{_entities: array} $data */ + $data = $result->data; + // The custom resolver for this schema, always adds 1 to the id and gets the next // episode for the sake of testing the ability to change the resolver in the configuration - $this->assertEquals('The Empire Strikes Back', $result->data['_entities'][0]['title']); + $this->assertEquals('The Empire Strikes Back', $data['_entities'][0]['title']); $this->assertMatchesSnapshot($result->toArray()); } } diff --git a/test/StarWarsData.php b/test/StarWarsData.php index 59ee2d0..c2f71a3 100644 --- a/test/StarWarsData.php +++ b/test/StarWarsData.php @@ -31,13 +31,13 @@ class StarWarsData private static array $locations; /** - * @return EpisodeType + * @return EpisodeType | null */ - public static function getEpisodeById(int $id): array + public static function getEpisodeById(int $id): ?array { $matches = array_filter(self::getEpisodes(), fn ($episode) => $episode['id'] === $id); - return reset($matches); + return reset($matches) ?: null; } /** diff --git a/test/StarWarsSchema.php b/test/StarWarsSchema.php index 332f0c4..55ce1f4 100644 --- a/test/StarWarsSchema.php +++ b/test/StarWarsSchema.php @@ -19,7 +19,7 @@ class StarWarsSchema public static function getEpisodesSchema(): FederatedSchema { - if (!self::$episodesSchema) { + if (!isset(self::$episodesSchema)) { self::$episodesSchema = new FederatedSchema([ 'query' => self::getQueryType(), ]); @@ -30,7 +30,7 @@ public static function getEpisodesSchema(): FederatedSchema public static function getEpisodesSchemaCustomResolver(): FederatedSchema { - if (!self::$overRiddedEpisodesSchema) { + if (!isset(self::$overRiddedEpisodesSchema)) { self::$overRiddedEpisodesSchema = new FederatedSchema([ 'query' => self::getQueryType(), 'resolve' => fn ($root, $args, $context, $info) => array_map(function ($ref) use ($info) { From a73c5adf7904a63cbed263815851d7aef0559e89 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 19:27:07 -0600 Subject: [PATCH 3/7] chore: add tooling for local tests and committing --- .gitignore | 3 +- composer.json | 104 ++++++++----- composer.lock | 400 +++++++++++++++++++++++++++++++++++++++++++++++++- package.json | 10 +- 4 files changed, 474 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 4e0b161..88aad12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ composer.phar +/build/ /vendor/ /node_modules/ *.cache cov.xml .idea -.vscode \ No newline at end of file +.vscode diff --git a/composer.json b/composer.json index 1d8d195..057494d 100644 --- a/composer.json +++ b/composer.json @@ -1,51 +1,14 @@ { "name": "skillshare/apollo-federation-php", "description": "A PHP port of the Apollo Federation specification.", - "type": "library", "license": "MIT", + "type": "library", "require": { "php": "^7.4 || ^8.0", "webonyx/graphql-php": "^14.0" }, - "scripts": { - "test": "phpunit", - "sync": "yarn install && composer update", - "commit": "yarn commit", - "dev:lint": [ - "@dev:lint:syntax", - "@dev:lint:style" - ], - "dev:lint:fix": "phpcbf", - "dev:lint:style": "phpcs --colors", - "dev:lint:syntax": "parallel-lint --colors src/ test/", - "dev:analyze": [ - "@dev:analyze:phpstan", - "@dev:analyze:psalm" - ], - "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G", - "dev:analyze:psalm": "psalm" - }, - "config": { - "preferred-install": "dist", - "sort-packages": true, - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true, - "phpstan/extension-installer": true - } - }, - "autoload": { - "psr-4": { - "Apollo\\Federation\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Apollo\\Federation\\Tests\\": "test/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true, "require-dev": { + "ergebnis/composer-normalize": "^2.28", "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "^1.3", "phpstan/extension-installer": "^1.2", @@ -58,5 +21,68 @@ "react/promise": "^2.7", "spatie/phpunit-snapshot-assertions": "^4.2", "vimeo/psalm": "^5.2" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "autoload": { + "psr-4": { + "Apollo\\Federation\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Apollo\\Federation\\Tests\\": "test/" + } + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true, + "ergebnis/composer-normalize": true + }, + "preferred-install": "dist", + "sort-packages": true + }, + "scripts": { + "commit": "yarn commit", + "dev:analyze": [ + "@dev:analyze:phpstan", + "@dev:analyze:psalm" + ], + "dev:analyze:phpstan": "phpstan analyse --ansi --memory-limit=1G", + "dev:analyze:psalm": "psalm", + "dev:lint": [ + "@dev:lint:syntax", + "@dev:lint:style" + ], + "dev:lint:fix": "phpcbf", + "dev:lint:style": "phpcs --colors", + "dev:lint:syntax": "parallel-lint --colors src/ test/", + "dev:test": [ + "@dev:lint", + "@dev:analyze", + "@dev:test:unit" + ], + "dev:test:coverage:ci": "phpunit --colors=always --coverage-text --coverage-clover build/coverage/clover.xml --coverage-cobertura build/coverage/cobertura.xml --coverage-crap4j build/coverage/crap4j.xml --coverage-xml build/coverage/coverage-xml --log-junit build/junit.xml", + "dev:test:coverage:html": "phpunit --colors=always --coverage-html build/coverage/coverage-html/", + "dev:test:unit": "phpunit --colors=always", + "sync": "yarn install && composer install", + "test": "@dev:test" + }, + "scripts-descriptions": { + "commit": "Runs specialized tooling for git commits.", + "dev:analyze": "Runs all static analysis checks.", + "dev:analyze:phpstan": "Runs the PHPStan static analyzer.", + "dev:analyze:psalm": "Runs the Psalm static analyzer.", + "dev:lint": "Runs all linting checks.", + "dev:lint:fix": "Auto-fixes coding standards issues, if possible.", + "dev:lint:style": "Checks for coding standards issues.", + "dev:lint:syntax": "Checks for syntax errors.", + "dev:test": "Runs linting, static analysis, and unit tests.", + "dev:test:coverage:ci": "Runs unit tests and generates CI coverage reports.", + "dev:test:coverage:html": "Runs unit tests and generates HTML coverage report.", + "dev:test:unit": "Runs unit tests.", + "sync": "Installs dependencies.", + "test": "Runs linting, static analysis, and unit tests." } } diff --git a/composer.lock b/composer.lock index 5c27b97..727d10d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "48991876e2fb384436b394d2403eaf6e", + "content-hash": "a01e53b093d162fe460a38a3d81ff3ef", "packages": [ { "name": "webonyx/graphql-php", @@ -713,6 +713,274 @@ ], "time": "2022-03-03T08:28:38+00:00" }, + { + "name": "ergebnis/composer-normalize", + "version": "2.28.3", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/composer-normalize.git", + "reference": "ec75a2bf751f6fec165e9ea0262655b8ca397e5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/ec75a2bf751f6fec165e9ea0262655b8ca397e5c", + "reference": "ec75a2bf751f6fec165e9ea0262655b8ca397e5c", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "ergebnis/json-normalizer": "~2.1.0", + "ergebnis/json-printer": "^3.2.0", + "justinrainbow/json-schema": "^5.2.12", + "localheinz/diff": "^1.1.1", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2.3.9", + "ergebnis/license": "^1.2.0", + "ergebnis/php-cs-fixer-config": "^4.4.0", + "fakerphp/faker": "^1.19.0", + "phpunit/phpunit": "^9.5.21", + "psalm/plugin-phpunit": "~0.17.0", + "symfony/filesystem": "^5.4.9", + "vimeo/psalm": "^4.24.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\Composer\\Normalize\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "Provides a composer plugin for normalizing composer.json.", + "homepage": "https://github.com/ergebnis/composer-normalize", + "keywords": [ + "composer", + "normalize", + "normalizer", + "plugin" + ], + "support": { + "issues": "https://github.com/ergebnis/composer-normalize/issues", + "source": "https://github.com/ergebnis/composer-normalize" + }, + "time": "2022-07-05T16:09:10+00:00" + }, + { + "name": "ergebnis/json-normalizer", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/json-normalizer.git", + "reference": "2039eb11131a243b9204bf51219baa08935e6b1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/json-normalizer/zipball/2039eb11131a243b9204bf51219baa08935e6b1d", + "reference": "2039eb11131a243b9204bf51219baa08935e6b1d", + "shasum": "" + }, + "require": { + "ergebnis/json-printer": "^3.2.0", + "ergebnis/json-schema-validator": "^2.0.0", + "ext-json": "*", + "justinrainbow/json-schema": "^5.2.11", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/data-provider": "^1.0.0", + "ergebnis/license": "^1.2.0", + "ergebnis/php-cs-fixer-config": "^3.4.0", + "fakerphp/faker": "^1.17.0", + "infection/infection": "~0.25.5", + "phpunit/phpunit": "^9.5.11", + "psalm/plugin-phpunit": "~0.16.1", + "vimeo/psalm": "^4.17.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ergebnis\\Json\\Normalizer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "Provides generic and vendor-specific normalizers for normalizing JSON documents.", + "homepage": "https://github.com/ergebnis/json-normalizer", + "keywords": [ + "json", + "normalizer" + ], + "support": { + "issues": "https://github.com/ergebnis/json-normalizer/issues", + "source": "https://github.com/ergebnis/json-normalizer" + }, + "funding": [ + { + "url": "https://github.com/localheinz", + "type": "github" + } + ], + "time": "2022-01-04T11:19:55+00:00" + }, + { + "name": "ergebnis/json-printer", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/json-printer.git", + "reference": "651cab2b7604a6b338d0d16749f5ea0851a68005" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/651cab2b7604a6b338d0d16749f5ea0851a68005", + "reference": "651cab2b7604a6b338d0d16749f5ea0851a68005", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/license": "^1.1.0", + "ergebnis/php-cs-fixer-config": "^3.4.0", + "fakerphp/faker": "^1.17.0", + "infection/infection": "~0.25.5", + "phpunit/phpunit": "^9.5.11", + "psalm/plugin-phpunit": "~0.16.1", + "vimeo/psalm": "^4.16.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Ergebnis\\Json\\Printer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "Provides a JSON printer, allowing for flexible indentation.", + "homepage": "https://github.com/ergebnis/json-printer", + "keywords": [ + "formatter", + "json", + "printer" + ], + "support": { + "issues": "https://github.com/ergebnis/json-printer/issues", + "source": "https://github.com/ergebnis/json-printer" + }, + "funding": [ + { + "url": "https://github.com/localheinz", + "type": "github" + } + ], + "time": "2021-12-27T12:39:13+00:00" + }, + { + "name": "ergebnis/json-schema-validator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/json-schema-validator.git", + "reference": "dacd8a47c1cc2c426ec71e952da3609ebe901fac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/json-schema-validator/zipball/dacd8a47c1cc2c426ec71e952da3609ebe901fac", + "reference": "dacd8a47c1cc2c426ec71e952da3609ebe901fac", + "shasum": "" + }, + "require": { + "ext-json": "*", + "justinrainbow/json-schema": "^5.2.10", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.18.0", + "ergebnis/data-provider": "^1.0.0", + "ergebnis/license": "^1.1.0", + "ergebnis/php-cs-fixer-config": "~3.4.0", + "fakerphp/faker": "^1.17.0", + "infection/infection": "~0.25.3", + "phpunit/phpunit": "~9.5.10", + "psalm/plugin-phpunit": "~0.16.1", + "vimeo/psalm": "^4.15.0" + }, + "type": "library", + "extra": { + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\Json\\SchemaValidator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com" + } + ], + "description": "Provides a JSON schema validator, building on top of justinrainbow/json-schema.", + "homepage": "https://github.com/ergebnis/json-schema-validator", + "keywords": [ + "json", + "schema", + "validator" + ], + "support": { + "issues": "https://github.com/ergebnis/json-schema-validator/issues", + "source": "https://github.com/ergebnis/json-schema-validator" + }, + "funding": [ + { + "url": "https://github.com/localheinz", + "type": "github" + } + ], + "time": "2021-12-13T16:54:56+00:00" + }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -875,6 +1143,136 @@ ], "time": "2022-12-10T21:26:31+00:00" }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.12", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12" + }, + "time": "2022-04-13T08:02:27+00:00" + }, + { + "name": "localheinz/diff", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/localheinz/diff.git", + "reference": "851bb20ea8358c86f677f5f111c4ab031b1c764c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/localheinz/diff/zipball/851bb20ea8358c86f677f5f111c4ab031b1c764c", + "reference": "851bb20ea8358c86f677f5f111c4ab031b1c764c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Fork of sebastian/diff for use with ergebnis/composer-normalize", + "homepage": "https://github.com/localheinz/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "source": "https://github.com/localheinz/diff/tree/main" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-07-06T04:49:32+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.0", diff --git a/package.json b/package.json index b719dd8..5f7d5ee 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,14 @@ } }, "lint-staged": { - "*": [ - "prettier --write", + "composer.json": [ + "composer normalize", + "git add" + ], + "{src,tests}/**/*.php": [ + "composer sk:lint:fix", + "composer sk:lint", + "composer sk:analyze", "git add" ] } From ac894c81321ad721e7894106b31a22c38db92f1f Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 19:33:03 -0600 Subject: [PATCH 4/7] chore: ignore coding standards violations for PHP 8.1 --- package.json | 6 +++--- src/FederatedSchema.php | 1 + src/Types/EntityObjectType.php | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5f7d5ee..0de48ad 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "git add" ], "{src,tests}/**/*.php": [ - "composer sk:lint:fix", - "composer sk:lint", - "composer sk:analyze", + "composer dev:lint:fix", + "composer dev:lint", + "composer dev:analyze", "git add" ] } diff --git a/src/FederatedSchema.php b/src/FederatedSchema.php index 471c1a1..08a3dfe 100644 --- a/src/FederatedSchema.php +++ b/src/FederatedSchema.php @@ -234,6 +234,7 @@ function ($root, array $args, $context, ResolveInfo $info) use ($config): array * * @psalm-suppress UnusedParam $root is unused here, but it might be used by another callable */ + // phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint private function resolve($root, array $args, $context, ResolveInfo $info): array { return array_map( diff --git a/src/Types/EntityObjectType.php b/src/Types/EntityObjectType.php index b4c4f30..bd2b95f 100644 --- a/src/Types/EntityObjectType.php +++ b/src/Types/EntityObjectType.php @@ -90,6 +90,7 @@ public function hasReferenceResolver(): bool * * @return mixed */ + // phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint public function resolveReference(?array $ref = null, $context = null, $info = null) { $this->validateReferenceResolver(); From 1d6d9af0bbc7d54c259609677278a17bd3946d88 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 19:43:14 -0600 Subject: [PATCH 5/7] ci: set up improved CI on GitHub Actions --- .github/workflows/build.yml | 20 -- .github/workflows/continuous-integration.yml | 158 +++++++++++++++ composer.lock | 192 +++++++------------ phpunit.xml.dist | 2 +- 4 files changed, 224 insertions(+), 148 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/continuous-integration.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 1d54f4f..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Build - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - - name: Validate composer.json and composer.lock - run: composer validate - - - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest - - - name: Run test suite - run: composer run-script test diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..16f5918 --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,158 @@ +# GitHub Actions Documentation: https://docs.github.com/en/actions + +name: "build" + +on: + push: + branches: + - "main" + tags: + - "*" + pull_request: + branches: + - "main" + +# Cancels all previous workflow runs for the same branch that have not yet completed. +concurrency: + # The concurrency group contains the workflow name and the branch name. + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +env: + COMPOSER_ROOT_VERSION: "1.99.99" + +jobs: + coding-standards: + name: "Coding standards" + runs-on: "ubuntu-latest" + + steps: + - name: "Checkout repository" + uses: "actions/checkout@v3.1.0" + + - name: "Install PHP" + uses: "shivammathur/setup-php@2.22.0" + with: + php-version: "8.2" + coverage: "none" + + - name: "Install dependencies (Composer)" + uses: "ramsey/composer-install@2.2.0" + + - name: "Check syntax (php-parallel-lint)" + run: "composer dev:lint:syntax" + + - name: "Check coding standards (PHP_CodeSniffer)" + run: "composer dev:lint:style" + + static-analysis: + name: "Static analysis" + runs-on: "ubuntu-latest" + + steps: + - name: "Checkout repository" + uses: "actions/checkout@v3.1.0" + + - name: "Install PHP" + uses: "shivammathur/setup-php@2.22.0" + with: + php-version: "8.2" + coverage: "none" + + - name: "Install dependencies (Composer)" + uses: "ramsey/composer-install@2.2.0" + + - name: "Statically analyze code (PHPStan)" + run: "composer dev:analyze:phpstan" + + - name: "Statically analyze code (Psalm)" + run: "composer dev:analyze:psalm -- --shepherd" + + security-analysis: + name: "Security analysis" + needs: ["coding-standards", "static-analysis"] + runs-on: "ubuntu-latest" + + steps: + - name: "Checkout repository" + uses: "actions/checkout@v3.1.0" + + - name: "Install PHP" + uses: "shivammathur/setup-php@2.22.0" + with: + php-version: "8.2" + coverage: "none" + + - name: "Install dependencies (Composer)" + uses: "ramsey/composer-install@2.2.0" + + - name: "Analyze security of code (Psalm)" + run: "./vendor/bin/psalm --taint-analysis --report=build/logs/psalm.sarif" + + - name: "Upload security analysis results to GitHub" + uses: "github/codeql-action/upload-sarif@v2" + with: + sarif_file: "build/logs/psalm.sarif" + + code-coverage: + name: "Code coverage" + needs: ["coding-standards", "static-analysis"] + runs-on: "${{ matrix.os }}" + + strategy: + matrix: + os: ["macos-latest", "ubuntu-latest", "windows-latest"] + + steps: + - name: "Checkout repository" + uses: "actions/checkout@v3.1.0" + + - name: "Install PHP" + uses: "shivammathur/setup-php@2.22.0" + with: + php-version: "8.2" + coverage: "pcov" + ini-values: "memory_limit=-1" + + - name: "Install dependencies (Composer)" + uses: "ramsey/composer-install@2.2.0" + + - name: "Run unit tests (PHPUnit)" + run: "composer dev:test:coverage:ci" + + - name: "Publish coverage report to Codecov" + uses: "codecov/codecov-action@v3.1.1" + + unit-tests: + name: "Unit tests" + needs: ["code-coverage"] + runs-on: "${{ matrix.os }}" + + strategy: + fail-fast: false + matrix: + php: + - "7.4" + - "8.0" + - "8.1" + - "8.2" + os: ["ubuntu-latest"] + composer-deps: ["lowest", "highest"] + + steps: + - name: "Checkout repository" + uses: "actions/checkout@v3.1.0" + + - name: "Install PHP" + uses: "shivammathur/setup-php@2.22.0" + with: + php-version: "${{ matrix.php }}" + coverage: "none" + + - name: "Install dependencies (Composer)" + uses: "ramsey/composer-install@2.2.0" + with: + dependency-versions: "${{ matrix.composer-deps }}" + + - name: "Run unit tests (PHPUnit)" + run: "composer dev:test:unit" diff --git a/composer.lock b/composer.lock index 727d10d..19f3217 100644 --- a/composer.lock +++ b/composer.lock @@ -1385,16 +1385,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.15.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", + "reference": "f59bbe44bf7d96f24f3e2b4ddc21cd52c1d2adbc", "shasum": "" }, "require": { @@ -1435,9 +1435,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.2" }, - "time": "2022-05-31T20:59:12+00:00" + "time": "2022-11-12T15:38:23+00:00" }, { "name": "openlss/lib-array2xml", @@ -2011,25 +2011,30 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "77a32518733312af16a44300404e945338981de3" + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", - "reference": "77a32518733312af16a44300404e945338981de3", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d", + "reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { "ext-tokenizer": "*", - "psalm/phar": "^4.8" + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { @@ -2055,76 +2060,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" - }, - "time": "2022-03-15T21:29:03+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "v1.15.0", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2 || ~8.0, <8.2", - "phpdocumentor/reflection-docblock": "^5.2", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0 || ^7.0", - "phpunit/phpunit": "^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2" }, - "time": "2021-12-08T12:19:24+00:00" + "time": "2022-10-14T12:47:21+00:00" }, { "name": "phpstan/extension-installer", @@ -2328,23 +2266,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.15", + "version": "9.2.21", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f" + "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f", - "reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/3f893e19712bb0c8bc86665d1562e9fd509c4ef0", + "reference": "3f893e19712bb0c8bc86665d1562e9fd509c4ef0", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^4.14", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -2393,7 +2331,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.21" }, "funding": [ { @@ -2401,7 +2339,7 @@ "type": "github" } ], - "time": "2022-03-07T09:28:20+00:00" + "time": "2022-12-14T13:26:54+00:00" }, { "name": "phpunit/php-file-iterator", @@ -2646,16 +2584,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.21", + "version": "9.5.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1" + "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1", - "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2bc7ffdca99f92d959b3f2270529334030bba38", + "reference": "a2bc7ffdca99f92d959b3f2270529334030bba38", "shasum": "" }, "require": { @@ -2670,7 +2608,6 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpspec/prophecy": "^1.12.1", "phpunit/php-code-coverage": "^9.2.13", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", @@ -2678,19 +2615,16 @@ "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.0", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, - "require-dev": { - "phpspec/prophecy-phpunit": "^2.0.1" - }, "suggest": { "ext-soap": "*", "ext-xdebug": "*" @@ -2732,7 +2666,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.27" }, "funding": [ { @@ -2742,9 +2676,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-06-19T12:14:25+00:00" + "time": "2022-12-09T07:31:23+00:00" }, { "name": "psalm/plugin-phpunit", @@ -3271,16 +3209,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", - "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", "shasum": "" }, "require": { @@ -3333,7 +3271,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" }, "funding": [ { @@ -3341,7 +3279,7 @@ "type": "github" } ], - "time": "2020-10-26T15:49:45+00:00" + "time": "2022-09-14T12:41:17+00:00" }, { "name": "sebastian/complexity", @@ -3531,16 +3469,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", - "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", "shasum": "" }, "require": { @@ -3596,7 +3534,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" }, "funding": [ { @@ -3604,7 +3542,7 @@ "type": "github" } ], - "time": "2021-11-11T14:18:36+00:00" + "time": "2022-09-14T06:03:37+00:00" }, { "name": "sebastian/global-state", @@ -3959,16 +3897,16 @@ }, { "name": "sebastian/type", - "version": "3.0.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad" + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", - "reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", + "reference": "fb3fe09c5f0bae6bc27ef3ce933a1e0ed9464b6e", "shasum": "" }, "require": { @@ -3980,7 +3918,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.2-dev" } }, "autoload": { @@ -4003,7 +3941,7 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.0.0" + "source": "https://github.com/sebastianbergmann/type/tree/3.2.0" }, "funding": [ { @@ -4011,7 +3949,7 @@ "type": "github" } ], - "time": "2022-03-15T09:54:48+00:00" + "time": "2022-09-12T14:47:03+00:00" }, { "name": "sebastian/version", @@ -4483,16 +4421,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -4507,7 +4445,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -4545,7 +4483,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -4561,7 +4499,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 1bc7cb9..cbb2ba2 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -25,7 +25,7 @@ - + From a08f9aac243dbc3c3686a3d691c49c75b7639db8 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 19:47:31 -0600 Subject: [PATCH 6/7] chore: bump the lowest versions of our dev requirements --- composer.json | 16 ++++++++-------- composer.lock | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/composer.json b/composer.json index 057494d..b11d63a 100644 --- a/composer.json +++ b/composer.json @@ -10,16 +10,16 @@ "require-dev": { "ergebnis/composer-normalize": "^2.28", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", + "php-parallel-lint/php-parallel-lint": "^1.3.2", "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^1.9.3", + "phpstan/phpstan-phpunit": "^1.3.2", + "phpunit/phpunit": "^9.5.27", "psalm/plugin-phpunit": "^0.18.4", - "psr/http-message": "^1.0", - "ramsey/coding-standard": "^2.0", - "react/promise": "^2.7", - "spatie/phpunit-snapshot-assertions": "^4.2", + "psr/http-message": "^1.0.1", + "ramsey/coding-standard": "^2.0.3", + "react/promise": "^2.9.0", + "spatie/phpunit-snapshot-assertions": "^4.2.16", "vimeo/psalm": "^5.2" }, "minimum-stability": "dev", diff --git a/composer.lock b/composer.lock index 19f3217..ee1fd4c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a01e53b093d162fe460a38a3d81ff3ef", + "content-hash": "20fe2be9027806f8375e84c21a99e0a2", "packages": [ { "name": "webonyx/graphql-php", @@ -4067,16 +4067,16 @@ }, { "name": "spatie/phpunit-snapshot-assertions", - "version": "4.2.14", + "version": "4.2.16", "source": { "type": "git", "url": "https://github.com/spatie/phpunit-snapshot-assertions.git", - "reference": "339db94d5a38b23782a49b9041194a8509bd1854" + "reference": "4c325139313c06b656ba10d5b60306c0de728c1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/phpunit-snapshot-assertions/zipball/339db94d5a38b23782a49b9041194a8509bd1854", - "reference": "339db94d5a38b23782a49b9041194a8509bd1854", + "url": "https://api.github.com/repos/spatie/phpunit-snapshot-assertions/zipball/4c325139313c06b656ba10d5b60306c0de728c1f", + "reference": "4c325139313c06b656ba10d5b60306c0de728c1f", "shasum": "" }, "require": { @@ -4123,7 +4123,7 @@ ], "support": { "issues": "https://github.com/spatie/phpunit-snapshot-assertions/issues", - "source": "https://github.com/spatie/phpunit-snapshot-assertions/tree/4.2.14" + "source": "https://github.com/spatie/phpunit-snapshot-assertions/tree/4.2.16" }, "funding": [ { @@ -4131,7 +4131,7 @@ "type": "custom" } ], - "time": "2022-07-29T14:54:35+00:00" + "time": "2022-10-10T15:58:50+00:00" }, { "name": "squizlabs/php_codesniffer", From 1082efa1a4a256d3548399579667b377ccc58756 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Wed, 14 Dec 2022 20:03:33 -0600 Subject: [PATCH 7/7] chore: only run coverage analysis on Ubuntu --- .github/workflows/continuous-integration.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 16f5918..04a1044 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -97,11 +97,7 @@ jobs: code-coverage: name: "Code coverage" needs: ["coding-standards", "static-analysis"] - runs-on: "${{ matrix.os }}" - - strategy: - matrix: - os: ["macos-latest", "ubuntu-latest", "windows-latest"] + runs-on: "ubuntu-latest" steps: - name: "Checkout repository"