From a36c979e349928dd2c06c9ab474f54b7ef715b32 Mon Sep 17 00:00:00 2001 From: Alex Bouma Date: Sat, 8 Mar 2025 14:32:09 +0100 Subject: [PATCH 01/19] Allow Laravel 12 --- .github/workflows/validate.yml | 20 ++++++++++--- CHANGELOG.md | 4 +++ composer.json | 34 +++++++++++++--------- src/Console/LighthouseGeneratorCommand.php | 2 +- src/Schema/Types/Scalars/Date.php | 2 +- src/Schema/Types/Scalars/DateScalar.php | 8 +++-- src/Schema/Types/Scalars/DateTime.php | 2 +- src/Schema/Types/Scalars/DateTimeTz.php | 2 +- src/Schema/Types/Scalars/DateTimeUtc.php | 4 +-- 9 files changed, 52 insertions(+), 26 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 03da633ad2..6e8b03e4ac 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -32,6 +32,7 @@ jobs: - "^9" - "^10" - "^11" + - "^12" composer: - name: lowest arg: "--prefer-lowest --prefer-stable" @@ -44,6 +45,10 @@ jobs: laravel-version: "^11" - php-version: "8.1" laravel-version: "^11" + - php-version: "8.0" + laravel-version: "^12" + - php-version: "8.1" + laravel-version: "^12" steps: - uses: actions/checkout@v4 @@ -63,7 +68,8 @@ jobs: - name: "Remove conflicting dependencies that are not needed here" run: composer remove --dev --no-update phpbench/phpbench rector/rector - - if: matrix.laravel-version != '^10' + - name: "Remove Pennant for Laravel 9 because it is not compatible" + if: matrix.laravel-version == '^9' run: composer remove --dev --no-update laravel/pennant - run: > @@ -94,6 +100,7 @@ jobs: - "^9" - "^10" - "^11" + - "^12" os: - ubuntu-latest composer: @@ -108,6 +115,10 @@ jobs: laravel-version: "^11" - php-version: "8.1" laravel-version: "^11" + - php-version: "8.0" + laravel-version: "^12" + - php-version: "8.1" + laravel-version: "^12" services: mysql: @@ -142,7 +153,8 @@ jobs: - name: "Remove conflicting dependencies that are not needed here" run: composer remove --dev --no-update larastan/larastan phpstan/phpstan-mockery phpbench/phpbench rector/rector - - if: matrix.laravel-version != '^10' && matrix.laravel-version != '^11' + - name: "Remove Pennant for Laravel 9 because it is not compatible" + if: matrix.laravel-version == '^9' run: composer remove --dev --no-update laravel/pennant - run: > @@ -162,7 +174,7 @@ jobs: strategy: matrix: php-version: ["8.4"] - laravel-version: ["^11"] + laravel-version: ["^12"] services: mysql: @@ -209,7 +221,7 @@ jobs: strategy: matrix: php-version: ["8.4"] - laravel-version: ["^11"] + laravel-version: ["^12"] steps: - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 580007cb07..9247868073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co ## Unreleased +### Added + +- Support Laravel 12 https://github.com/nuwave/lighthouse/pull/2665 + ## v6.50.0 ### Added diff --git a/composer.json b/composer.json index 1d273823d7..0e593620bf 100644 --- a/composer.json +++ b/composer.json @@ -29,15 +29,15 @@ "php": "^8", "ext-json": "*", "haydenpierce/class-finder": "^0.4 || ^0.5", - "illuminate/auth": "^9 || ^10 || ^11", - "illuminate/bus": "^9 || ^10 || ^11", - "illuminate/contracts": "^9 || ^10 || ^11", - "illuminate/http": "^9 || ^10 || ^11", - "illuminate/pagination": "^9 || ^10 || ^11", - "illuminate/queue": "^9 || ^10 || ^11", - "illuminate/routing": "^9 || ^10 || ^11", - "illuminate/support": "^9 || ^10 || ^11", - "illuminate/validation": "^9 || ^10 || ^11", + "illuminate/auth": "^9 || ^10 || ^11 || ^12", + "illuminate/bus": "^9 || ^10 || ^11 || ^12", + "illuminate/contracts": "^9 || ^10 || ^11 || ^12", + "illuminate/http": "^9 || ^10 || ^11 || ^12", + "illuminate/pagination": "^9 || ^10 || ^11 || ^12", + "illuminate/queue": "^9 || ^10 || ^11 || ^12", + "illuminate/routing": "^9 || ^10 || ^11 || ^12", + "illuminate/support": "^9 || ^10 || ^11 || ^12", + "illuminate/validation": "^9 || ^10 || ^11 || ^12", "laragraph/utils": "^1.5 || ^2", "thecodingmachine/safe": "^1 || ^2 || ^3", "webonyx/graphql-php": "^15" @@ -45,21 +45,21 @@ "require-dev": { "algolia/algoliasearch-client-php": "^3", "bensampo/laravel-enum": "^5 || ^6", - "dms/phpunit-arraysubset-asserts": "^0.4 || ^0.5", + "dms/phpunit-arraysubset-asserts": "^0.4 || ^0.5 || dev-add-phpunit-11-support", "ergebnis/composer-normalize": "^2.2.2", "fakerphp/faker": "^1.21", "google/protobuf": "^3.21", "larastan/larastan": "^2.9.14 || ^3.0.4", - "laravel/framework": "^9 || ^10 || ^11", + "laravel/framework": "^9 || ^10 || ^11 || ^12", "laravel/legacy-factories": "^1.1.1", "laravel/pennant": "^1", - "laravel/scout": "^8 || ^9 || ^10 || ^11", + "laravel/scout": "^8 || ^9 || ^10", "mattiasgeniar/phpunit-query-count-assertions": "^1.1", "mll-lab/graphql-php-scalars": "^6", "mll-lab/php-cs-fixer-config": "^5", "mockery/mockery": "^1.5", - "nesbot/carbon": "^2.62.1", - "orchestra/testbench": "^7.50 || ^8.32 || ^9.10", + "nesbot/carbon": "^2.62.1 || ^3.8.4", + "orchestra/testbench": "^7.50 || ^8.32 || ^9.10 || ^10.1", "phpbench/phpbench": "^1.2.6", "phpstan/extension-installer": "^1", "phpstan/phpstan": "^1.12.18 || ^2", @@ -80,6 +80,12 @@ "mll-lab/laravel-graphiql": "A graphical interactive in-browser GraphQL IDE - integrated with Laravel", "pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/pieterocp/phpunit-arraysubset-asserts" + } + ], "minimum-stability": "dev", "prefer-stable": true, "autoload": { diff --git a/src/Console/LighthouseGeneratorCommand.php b/src/Console/LighthouseGeneratorCommand.php index 0bad1f0b5d..f8c0f2f5b2 100644 --- a/src/Console/LighthouseGeneratorCommand.php +++ b/src/Console/LighthouseGeneratorCommand.php @@ -15,7 +15,7 @@ abstract class LighthouseGeneratorCommand extends GeneratorCommand */ protected function getNameInput(): string { - $name = $this->argument('name'); + $name = (string) $this->argument('name'); return ucfirst(trim($name)); } diff --git a/src/Schema/Types/Scalars/Date.php b/src/Schema/Types/Scalars/Date.php index 806eedf662..701e9ded3e 100644 --- a/src/Schema/Types/Scalars/Date.php +++ b/src/Schema/Types/Scalars/Date.php @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string return $carbon->toDateString(); } - protected function parse(mixed $value): Carbon + protected function parse(string $value): Carbon { // @phpstan-ignore-next-line We know the format to be good, so this can never return `false` return Carbon::createFromFormat('Y-m-d', $value)->startOfDay(); diff --git a/src/Schema/Types/Scalars/DateScalar.php b/src/Schema/Types/Scalars/DateScalar.php index 2b07a07c08..a42875cf7f 100644 --- a/src/Schema/Types/Scalars/DateScalar.php +++ b/src/Schema/Types/Scalars/DateScalar.php @@ -80,6 +80,10 @@ protected function tryParsingDate(mixed $value, string $exceptionClass): Illumin } } + if (! is_string($value)) { + throw new $exceptionClass('Query error: Can only parse strings.'); + } + return $this->parse($value); } catch (\Exception $exception) { throw new $exceptionClass($exception->getMessage()); @@ -92,7 +96,7 @@ abstract protected function format(IlluminateCarbon $carbon): string; /** * Try turning a client value into a Carbon instance. * - * @param mixed $value a possibly faulty client value + * @param string $value a possibly faulty client value */ - abstract protected function parse(mixed $value): IlluminateCarbon; + abstract protected function parse(string $value): IlluminateCarbon; } diff --git a/src/Schema/Types/Scalars/DateTime.php b/src/Schema/Types/Scalars/DateTime.php index 4b22ae82f3..4b3308c325 100644 --- a/src/Schema/Types/Scalars/DateTime.php +++ b/src/Schema/Types/Scalars/DateTime.php @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string return $carbon->toDateTimeString(); } - protected function parse(mixed $value): Carbon + protected function parse(string $value): Carbon { // @phpstan-ignore-next-line We know the format to be good, so this can never return `false` return Carbon::createFromFormat(Carbon::DEFAULT_TO_STRING_FORMAT, $value); diff --git a/src/Schema/Types/Scalars/DateTimeTz.php b/src/Schema/Types/Scalars/DateTimeTz.php index 34491bdf58..2b129db9bc 100644 --- a/src/Schema/Types/Scalars/DateTimeTz.php +++ b/src/Schema/Types/Scalars/DateTimeTz.php @@ -11,7 +11,7 @@ protected function format(Carbon $carbon): string return $carbon->toIso8601String(); } - protected function parse(mixed $value): Carbon + protected function parse(string $value): Carbon { // @phpstan-ignore-next-line We know the format to be good, so this can never return `false` return Carbon::createFromFormat( diff --git a/src/Schema/Types/Scalars/DateTimeUtc.php b/src/Schema/Types/Scalars/DateTimeUtc.php index 53db7c456f..64f55a2908 100644 --- a/src/Schema/Types/Scalars/DateTimeUtc.php +++ b/src/Schema/Types/Scalars/DateTimeUtc.php @@ -5,7 +5,7 @@ use Illuminate\Support\Carbon; /** - * Only works with Carbon 2. + * Works with Carbon 2.x & 3.x. */ class DateTimeUtc extends DateScalar { @@ -14,7 +14,7 @@ protected function format(Carbon $carbon): string return $carbon->toJSON(); } - protected function parse(mixed $value): Carbon + protected function parse(string $value): Carbon { // @phpstan-ignore-next-line We know the format to be good, so this can never return `false` return Carbon::createFromIsoFormat('YYYY-MM-DDTHH:mm:ss.SSSSSSZ', $value); From c84247deefc77ef043f8ce5e8e1a289816e84c23 Mon Sep 17 00:00:00 2001 From: stayallive <1090754+stayallive@users.noreply.github.com> Date: Thu, 20 Mar 2025 08:40:32 +0000 Subject: [PATCH 02/19] Apply proto changes --- .../Proto/ContextualizedStats.php | 6 +-- .../FederatedTracing/Proto/FieldStat.php | 13 ++--- .../Proto/InputFieldStats.php | 4 +- .../FederatedTracing/Proto/LimitsStats.php | 4 +- .../Proto/QueryLatencyStats.php | 2 +- .../FederatedTracing/Proto/ReportHeader.php | 12 +++-- src/Tracing/FederatedTracing/Proto/Trace.php | 50 ++++++++++--------- .../FederatedTracing/Proto/Trace/Limits.php | 4 +- .../FederatedTracing/Proto/Trace/Node.php | 10 ++-- .../Proto/Trace/QueryPlanNode/FetchNode.php | 6 +-- .../FederatedTracing/Proto/TracesAndStats.php | 3 +- 11 files changed, 61 insertions(+), 53 deletions(-) diff --git a/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php b/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php index d9a1a0ea84..6c01d7538f 100644 --- a/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php +++ b/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php @@ -67,11 +67,11 @@ class ContextualizedStats extends \Google\Protobuf\Internal\Message * Key is type name. This structure provides data for the count and latency of individual * field executions and thus only reflects operations for which field-level tracing occurred. * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\ExtendedReferences $extended_references - * Extended references including input types and enum values + * Extended references including input types and enum values. * @var array|\Google\Protobuf\Internal\MapField $local_per_type_stat - * Per type stats that are obtained directly by the router or gateway rather than FTV1 + * Per type stats that are obtained directly by the router or gateway rather than FTV1. * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\LimitsStats $limits_stats - * Stats that contain limits information for the query + * Stats that contain limits information for the query. * @var int|string $operation_count * Total number of operations processed during this period for this context. This includes all operations, even if they are sampled * and not included in the query latency stats. diff --git a/src/Tracing/FederatedTracing/Proto/FieldStat.php b/src/Tracing/FederatedTracing/Proto/FieldStat.php index eeeaa606d2..15c5bfc09d 100644 --- a/src/Tracing/FederatedTracing/Proto/FieldStat.php +++ b/src/Tracing/FederatedTracing/Proto/FieldStat.php @@ -82,16 +82,17 @@ class FieldStat extends \Google\Protobuf\Internal\Message * * @var string $return_type * required; eg "String!" for User.email:String! - * @var int|string $errors_count + * + * @type int|string $errors_count * Number of errors whose path is this field. Note that we assume that error * tracking does *not* require field-level instrumentation so this *will* * include errors from requests that don't contribute to the * `observed_execution_count` field (and does not need to be scaled by * field_execution_weight). - * @var int|string $observed_execution_count + * @type int|string $observed_execution_count * Number of times that the resolver for this field is directly observed being - * executed - * @var int|string $estimated_execution_count + * executed. + * @type int|string $estimated_execution_count * Same as `observed_execution_count` but potentially scaled upwards if the server was only * performing field-level instrumentation on a sampling of operations. For * example, if the server randomly instruments 1% of requests for this @@ -100,13 +101,13 @@ class FieldStat extends \Google\Protobuf\Internal\Message * this number goes up by the trace's `field_execution_weight` for each * observed field execution, while `observed_execution_count` above goes * up by 1.) - * @var int|string $requests_with_errors_count + * @type int|string $requests_with_errors_count * Number of times the resolver for this field is executed that resulted in * at least one error. "Request" is a misnomer here as this corresponds to * resolver calls, not overall operations. Like `errors_count` above, this * includes all requests rather than just requests with field-level * instrumentation. - * @var array|array|\Google\Protobuf\Internal\RepeatedField $latency_count + * @type array|array|\Google\Protobuf\Internal\RepeatedField $latency_count * Duration histogram for the latency of this field. Note that it is scaled in * the same way as estimated_execution_count so its "total count" might be * greater than `observed_execution_count` and may not exactly equal diff --git a/src/Tracing/FederatedTracing/Proto/InputFieldStats.php b/src/Tracing/FederatedTracing/Proto/InputFieldStats.php index d4e491c314..4f7ea6cc51 100644 --- a/src/Tracing/FederatedTracing/Proto/InputFieldStats.php +++ b/src/Tracing/FederatedTracing/Proto/InputFieldStats.php @@ -39,9 +39,9 @@ class InputFieldStats extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var int|string $refs - * The total number of operations that reference the input object field + * The total number of operations that reference the input object field. * @var int|string $null_refs - * The number of operations that reference the input object field as a null value + * The number of operations that reference the input object field as a null value. * @var int|string $missing * The number of operations that don't reference this input object field (the field is missing or undefined). * } diff --git a/src/Tracing/FederatedTracing/Proto/LimitsStats.php b/src/Tracing/FederatedTracing/Proto/LimitsStats.php index db6f6bcc36..b0276468e9 100644 --- a/src/Tracing/FederatedTracing/Proto/LimitsStats.php +++ b/src/Tracing/FederatedTracing/Proto/LimitsStats.php @@ -85,10 +85,10 @@ class LimitsStats extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var string $strategy - * The strategy used in cost calculations + * The strategy used in cost calculations. * @var array|array|\Google\Protobuf\Internal\RepeatedField $cost_estimated * The estimated cost as calculated via the strategy specified in stats context - * The reason that this is a histogram rather than fixed cost is that it can be affected by paging variables + * The reason that this is a histogram rather than fixed cost is that it can be affected by paging variables. * @var int|string $max_cost_estimated * The maximum estimated cost of the query * @var array|array|\Google\Protobuf\Internal\RepeatedField $cost_actual diff --git a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php index 7c1835a58d..7fb49e3c03 100644 --- a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php +++ b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php @@ -107,7 +107,7 @@ class QueryLatencyStats extends \Google\Protobuf\Internal\Message * @var int|string $persisted_query_misses * @var array|array|\Google\Protobuf\Internal\RepeatedField $cache_latency_count * This array includes the latency buckets for all operations included in cache_hits - * See comment on latency_count for details + * See comment on latency_count for details. * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\PathErrorStats $root_error_stats * Paths and counts for each error. The total number of requests with errors within this object should be the same as * requests_with_errors_count below. diff --git a/src/Tracing/FederatedTracing/Proto/ReportHeader.php b/src/Tracing/FederatedTracing/Proto/ReportHeader.php index 993e32baad..facca667cb 100644 --- a/src/Tracing/FederatedTracing/Proto/ReportHeader.php +++ b/src/Tracing/FederatedTracing/Proto/ReportHeader.php @@ -78,16 +78,18 @@ class ReportHeader extends \Google\Protobuf\Internal\Message * * @var string $graph_ref * eg "mygraph@myvariant" - * @var string $hostname + * + * @type string $hostname * eg "host-01.example.com" - * @var string $agent_version + * @type string $agent_version * eg "engineproxy 0.1.0" - * @var string $service_version + * @type string $service_version * eg "prod-4279-20160804T065423Z-5-g3cf0aa8" (taken from `git describe --tags`) - * @var string $runtime_version + * @type string $runtime_version * eg "node v4.6.0" - * @var string $uname + * @type string $uname * eg "Linux box 4.6.5-1-ec2 #1 SMP Mon Aug 1 02:31:38 PDT 2016 x86_64 GNU/Linux" + * * @var string $executable_schema_id * An id that is used to represent the schema to Apollo Graph Manager * Using this in place of what used to be schema_hash, since that is no longer diff --git a/src/Tracing/FederatedTracing/Proto/Trace.php b/src/Tracing/FederatedTracing/Proto/Trace.php index 0b5f445ee1..b8e552a1dd 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace.php +++ b/src/Tracing/FederatedTracing/Proto/Trace.php @@ -177,16 +177,17 @@ class Trace extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var \Google\Protobuf\Timestamp $start_time - * Wallclock time when the trace began + * Wallclock time when the trace began. * @var \Google\Protobuf\Timestamp $end_time - * Wallclock time when the trace ended + * Wallclock time when the trace ended. * @var int|string $duration_ns * High precision duration of the trace; may not equal end_time-start_time - * (eg, if your machine's clock changed during the trace) - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Node $root + * (eg, if your machine's clock changed during the trace). + * + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Node $root * A tree containing information about all resolvers run directly by this - * service, including errors - * @var bool $is_incomplete + * service, including errors. + * @type bool $is_incomplete * If this is true, the trace is potentially missing some nodes that were * present on the query plan. This can happen if the trace span buffer used * in the Router fills up and some spans have to be dropped. In these cases @@ -194,7 +195,7 @@ class Trace extends \Google\Protobuf\Internal\Message * be missing some referenced or executed fields, and some nodes may be * missing. If this is true we should display a warning to the user when they * view the trace in Explorer. - * @var string $signature + * @type string $signature * In addition to details.raw_query, we include a "signature" of the query, * which can be normalized: for example, you may want to discard aliases, drop * unused operations and fragments, sort fields, etc. The most important thing @@ -205,43 +206,44 @@ class Trace extends \Google\Protobuf\Internal\Message * that signature is in the key of traces_per_query rather than in this field. * Engineproxy provides the signature in legacy_signature_needs_resigning * instead. - * @var string $unexecutedOperationBody + * @type string $unexecutedOperationBody * Optional: when GraphQL parsing or validation against the GraphQL schema fails, these fields * can include reference to the operation being sent for users to dig into the set of operations - * that are failing validation - * @var string $unexecutedOperationName - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Details $details - * @var string $client_name - * @var string $client_version - * @var string $operation_type - * @var string $operation_subtype - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\HTTP $http - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\CachePolicy $cache_policy - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\QueryPlanNode $query_plan + * that are failing validation. + * @type string $unexecutedOperationName + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Details $details + * @type string $client_name + * @type string $client_version + * @type string $operation_type + * @type string $operation_subtype + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\HTTP $http + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\CachePolicy $cache_policy + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\QueryPlanNode $query_plan * If this Trace was created by a Router/Gateway, this is the query plan, including * sub-Traces for subgraphs. Note that the 'root' tree on the * top-level Trace won't contain any resolvers (though it could contain errors * that occurred in the Router/Gateway itself). - * @var bool $full_query_cache_hit + * @type bool $full_query_cache_hit * Was this response served from a full query response cache? (In that case * the node tree will have no resolvers.) - * @var bool $persisted_query_hit + * @type bool $persisted_query_hit * Was this query specified successfully as a persisted query hash? - * @var bool $persisted_query_register + * @type bool $persisted_query_register * Did this query contain both a full query string and a persisted query hash? * (This typically means that a previous request was rejected as an unknown * persisted query.) - * @var bool $registered_operation + * @type bool $registered_operation * Was this operation registered and a part of the safelist? - * @var bool $forbidden_operation + * @type bool $forbidden_operation * Was this operation forbidden due to lack of safelisting? - * @var float $field_execution_weight + * @type float $field_execution_weight * Some servers don't do field-level instrumentation for every request and assign * each request a "weight" for each request that they do instrument. When this * trace is aggregated into field usage stats, it should count as this value * towards the estimated_execution_count rather than just 1. This value should * typically be at least 1. * 0 is treated as 1 for backwards compatibility. + * * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Limits $limits * The limits information of the query. * } diff --git a/src/Tracing/FederatedTracing/Proto/Trace/Limits.php b/src/Tracing/FederatedTracing/Proto/Trace/Limits.php index 6fb7c2f3be..5232fc527e 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/Limits.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/Limits.php @@ -76,9 +76,9 @@ class Limits extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var string $result - * The result of the operation + * The result of the operation. * @var string $strategy - * The strategy used in cost calculations + * The strategy used in cost calculations. * @var int|string $cost_estimated * The estimated cost as calculated via the strategy specified in strategy * @var int|string $cost_actual diff --git a/src/Tracing/FederatedTracing/Proto/Trace/Node.php b/src/Tracing/FederatedTracing/Proto/Trace/Node.php index c8e93bc5bf..d5c8502457 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/Node.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/Node.php @@ -71,13 +71,15 @@ class Node extends \Google\Protobuf\Internal\Message * @var string $original_field_name * @var string $type * The field's return type; e.g. "String!" for User.email:String! - * @var string $parent_type + * + * @type string $parent_type * The field's parent type; e.g. "User" for User.email:String! - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\CachePolicy $cache_policy - * @var int|string $start_time + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\CachePolicy $cache_policy + * @type int|string $start_time * relative to the trace's start_time, in ns - * @var int|string $end_time + * @type int|string $end_time * relative to the trace's start_time, in ns + * * @var array<\Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Error>|\Google\Protobuf\Internal\RepeatedField $error * @var array<\Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Node>|\Google\Protobuf\Internal\RepeatedField $child * } diff --git a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php index edc48c8653..f1a4a029fa 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php @@ -66,12 +66,12 @@ class FetchNode extends \Google\Protobuf\Internal\Message * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace $trace * This Trace only contains start_time, end_time, duration_ns, and root; * all timings were calculated **on the subgraph**, and clock skew - * will be handled by the ingress server + * will be handled by the ingress server. * @var int|string $sent_time_offset - * relative to the outer trace's start_time, in ns, measured in the Router/Gateway + * relative to the outer trace's start_time, in ns, measured in the Router/Gateway. * @var \Google\Protobuf\Timestamp $sent_time * Wallclock times measured in the Router/Gateway for when this operation was - * sent and received + * sent and received. * @var \Google\Protobuf\Timestamp $received_time * } */ diff --git a/src/Tracing/FederatedTracing/Proto/TracesAndStats.php b/src/Tracing/FederatedTracing/Proto/TracesAndStats.php index af59bbf3e0..0c8607c8f7 100644 --- a/src/Tracing/FederatedTracing/Proto/TracesAndStats.php +++ b/src/Tracing/FederatedTracing/Proto/TracesAndStats.php @@ -59,7 +59,8 @@ class TracesAndStats extends \Google\Protobuf\Internal\Message * `@include` or `@skip`, etc). It also may be missing fields that show up in FieldStats * (as FieldStats will include the concrete object type for fields referenced * via an interface type). - * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\QueryMetadata $query_metadata + * + * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\QueryMetadata $query_metadata * This is an optional field that is used to provide more context to the key of this object within the * traces_per_query map. If it's omitted, we assume the key is a standard operation name and signature key. * } From 377831488993a69e386cba2a1226d9807af3d59a Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 11:38:22 +0100 Subject: [PATCH 03/19] Apply php-cs-fixer --- src/Tracing/FederatedTracing/Proto/ContextualizedStats.php | 6 +++--- src/Tracing/FederatedTracing/Proto/InputFieldStats.php | 4 ++-- src/Tracing/FederatedTracing/Proto/LimitsStats.php | 4 ++-- src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php | 2 +- src/Tracing/FederatedTracing/Proto/Trace.php | 6 +++--- src/Tracing/FederatedTracing/Proto/Trace/Limits.php | 4 ++-- .../Proto/Trace/QueryPlanNode/FetchNode.php | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php b/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php index 6c01d7538f..d9a1a0ea84 100644 --- a/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php +++ b/src/Tracing/FederatedTracing/Proto/ContextualizedStats.php @@ -67,11 +67,11 @@ class ContextualizedStats extends \Google\Protobuf\Internal\Message * Key is type name. This structure provides data for the count and latency of individual * field executions and thus only reflects operations for which field-level tracing occurred. * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\ExtendedReferences $extended_references - * Extended references including input types and enum values. + * Extended references including input types and enum values * @var array|\Google\Protobuf\Internal\MapField $local_per_type_stat - * Per type stats that are obtained directly by the router or gateway rather than FTV1. + * Per type stats that are obtained directly by the router or gateway rather than FTV1 * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\LimitsStats $limits_stats - * Stats that contain limits information for the query. + * Stats that contain limits information for the query * @var int|string $operation_count * Total number of operations processed during this period for this context. This includes all operations, even if they are sampled * and not included in the query latency stats. diff --git a/src/Tracing/FederatedTracing/Proto/InputFieldStats.php b/src/Tracing/FederatedTracing/Proto/InputFieldStats.php index 4f7ea6cc51..d4e491c314 100644 --- a/src/Tracing/FederatedTracing/Proto/InputFieldStats.php +++ b/src/Tracing/FederatedTracing/Proto/InputFieldStats.php @@ -39,9 +39,9 @@ class InputFieldStats extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var int|string $refs - * The total number of operations that reference the input object field. + * The total number of operations that reference the input object field * @var int|string $null_refs - * The number of operations that reference the input object field as a null value. + * The number of operations that reference the input object field as a null value * @var int|string $missing * The number of operations that don't reference this input object field (the field is missing or undefined). * } diff --git a/src/Tracing/FederatedTracing/Proto/LimitsStats.php b/src/Tracing/FederatedTracing/Proto/LimitsStats.php index b0276468e9..db6f6bcc36 100644 --- a/src/Tracing/FederatedTracing/Proto/LimitsStats.php +++ b/src/Tracing/FederatedTracing/Proto/LimitsStats.php @@ -85,10 +85,10 @@ class LimitsStats extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var string $strategy - * The strategy used in cost calculations. + * The strategy used in cost calculations * @var array|array|\Google\Protobuf\Internal\RepeatedField $cost_estimated * The estimated cost as calculated via the strategy specified in stats context - * The reason that this is a histogram rather than fixed cost is that it can be affected by paging variables. + * The reason that this is a histogram rather than fixed cost is that it can be affected by paging variables * @var int|string $max_cost_estimated * The maximum estimated cost of the query * @var array|array|\Google\Protobuf\Internal\RepeatedField $cost_actual diff --git a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php index 7fb49e3c03..7c1835a58d 100644 --- a/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php +++ b/src/Tracing/FederatedTracing/Proto/QueryLatencyStats.php @@ -107,7 +107,7 @@ class QueryLatencyStats extends \Google\Protobuf\Internal\Message * @var int|string $persisted_query_misses * @var array|array|\Google\Protobuf\Internal\RepeatedField $cache_latency_count * This array includes the latency buckets for all operations included in cache_hits - * See comment on latency_count for details. + * See comment on latency_count for details * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\PathErrorStats $root_error_stats * Paths and counts for each error. The total number of requests with errors within this object should be the same as * requests_with_errors_count below. diff --git a/src/Tracing/FederatedTracing/Proto/Trace.php b/src/Tracing/FederatedTracing/Proto/Trace.php index b8e552a1dd..115ea74632 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace.php +++ b/src/Tracing/FederatedTracing/Proto/Trace.php @@ -177,12 +177,12 @@ class Trace extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var \Google\Protobuf\Timestamp $start_time - * Wallclock time when the trace began. + * Wallclock time when the trace began * @var \Google\Protobuf\Timestamp $end_time - * Wallclock time when the trace ended. + * Wallclock time when the trace ended * @var int|string $duration_ns * High precision duration of the trace; may not equal end_time-start_time - * (eg, if your machine's clock changed during the trace). + * (eg, if your machine's clock changed during the trace) * * @type \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace\Node $root * A tree containing information about all resolvers run directly by this diff --git a/src/Tracing/FederatedTracing/Proto/Trace/Limits.php b/src/Tracing/FederatedTracing/Proto/Trace/Limits.php index 5232fc527e..6fb7c2f3be 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/Limits.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/Limits.php @@ -76,9 +76,9 @@ class Limits extends \Google\Protobuf\Internal\Message * Optional. Data for populating the Message object. * * @var string $result - * The result of the operation. + * The result of the operation * @var string $strategy - * The strategy used in cost calculations. + * The strategy used in cost calculations * @var int|string $cost_estimated * The estimated cost as calculated via the strategy specified in strategy * @var int|string $cost_actual diff --git a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php index f1a4a029fa..edc48c8653 100644 --- a/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php +++ b/src/Tracing/FederatedTracing/Proto/Trace/QueryPlanNode/FetchNode.php @@ -66,12 +66,12 @@ class FetchNode extends \Google\Protobuf\Internal\Message * @var \Nuwave\Lighthouse\Tracing\FederatedTracing\Proto\Trace $trace * This Trace only contains start_time, end_time, duration_ns, and root; * all timings were calculated **on the subgraph**, and clock skew - * will be handled by the ingress server. + * will be handled by the ingress server * @var int|string $sent_time_offset - * relative to the outer trace's start_time, in ns, measured in the Router/Gateway. + * relative to the outer trace's start_time, in ns, measured in the Router/Gateway * @var \Google\Protobuf\Timestamp $sent_time * Wallclock times measured in the Router/Gateway for when this operation was - * sent and received. + * sent and received * @var \Google\Protobuf\Timestamp $received_time * } */ From b7221601fcc64fbae5fb131483565a6c3c96acdf Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 11:43:12 +0100 Subject: [PATCH 04/19] Fix BelongsToMany pivot --- phpstan.neon | 3 ++- tests/Utils/Models/Category.php | 2 +- tests/Utils/Models/Post.php | 2 +- tests/Utils/Models/Role.php | 5 ++--- tests/Utils/Models/User.php | 5 ++--- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 3b204a2060..5760c97eb0 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -48,7 +48,8 @@ parameters: # Laravel 11 changes a lot of generics - '#generic class (Illuminate\\Database\\Eloquent\\Builder|Laravel\\Scout\\Builder)( but)? does not specify its types#' - '#contains generic type Illuminate\\Testing\\TestResponse<.+> but class Illuminate\\Testing\\TestResponse is not generic\.#' - - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<.+, .+> in PHPDoc tag @return specifies 2 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' + - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|MorphOne|MorphMany|MorphToMany)<.+, .+> in PHPDoc tag @return specifies 2 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' + - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(BelongsToMany)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 2: TRelatedModel, TDeclaringModel#' - '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<(.+), .+> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2>.#' - '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(BelongsTo|MorphTo)<(.+), \$this\((.+)\)> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2, \3>.#' - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' diff --git a/tests/Utils/Models/Category.php b/tests/Utils/Models/Category.php index 6addd916bd..6f3a2bad7d 100644 --- a/tests/Utils/Models/Category.php +++ b/tests/Utils/Models/Category.php @@ -28,7 +28,7 @@ public function parent(): BelongsTo return $this->belongsTo(Category::class, 'parent_id'); } - /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Post, $this> */ + /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Post, $this, \Illuminate\Database\Eloquent\Relations\Pivot> */ public function posts(): BelongsToMany { return $this->belongsToMany(Post::class, 'category_post', 'post_id', 'category_id'); diff --git a/tests/Utils/Models/Post.php b/tests/Utils/Models/Post.php index a27497ed32..6fb42e6aea 100644 --- a/tests/Utils/Models/Post.php +++ b/tests/Utils/Models/Post.php @@ -64,7 +64,7 @@ public function activity(): MorphMany return $this->morphMany(Activity::class, 'content'); } - /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Category, $this> */ + /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Category, $this, \Illuminate\Database\Eloquent\Relations\Pivot> */ public function categories(): BelongsToMany { return $this->belongsToMany(Category::class, 'category_post', 'category_id', 'post_id'); diff --git a/tests/Utils/Models/Role.php b/tests/Utils/Models/Role.php index 7b4ed57194..50911fa4ca 100644 --- a/tests/Utils/Models/Role.php +++ b/tests/Utils/Models/Role.php @@ -25,11 +25,10 @@ final class Role extends Model { public $timestamps = false; - /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\User, $this> */ + /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\User, $this, \Illuminate\Database\Eloquent\Relations\Pivot> */ public function users(): BelongsToMany { - return $this - ->belongsToMany(User::class) + return $this->belongsToMany(User::class) ->withPivot('meta'); } diff --git a/tests/Utils/Models/User.php b/tests/Utils/Models/User.php index c3748d3f70..bd636ec83a 100644 --- a/tests/Utils/Models/User.php +++ b/tests/Utils/Models/User.php @@ -103,11 +103,10 @@ public function posts(): HasMany return $this->hasMany(Post::class); } - /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Role, $this> */ + /** @return \Illuminate\Database\Eloquent\Relations\BelongsToMany<\Tests\Utils\Models\Role, $this, \Illuminate\Database\Eloquent\Relations\Pivot> */ public function roles(): BelongsToMany { - return $this - ->belongsToMany(Role::class) + return $this->belongsToMany(Role::class) ->withPivot('meta'); } From e3595f652b505131be9854d3ed72aa53bfcb25ab Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 11:47:17 +0100 Subject: [PATCH 05/19] fix mixin recognition --- phpstan.neon | 5 ++++- src/Testing/TestResponseMixin.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 5760c97eb0..c1fae4f9a9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -34,7 +34,7 @@ parameters: - path: tests/database/factories message: '#Variable \$factory might not be defined#' - # Mixins are magical + # Mixins magically rebind $this to be something else and do other funky stuff - path: src/Testing/TestResponseMixin.php message: '#Method Nuwave\\Lighthouse\\Testing\\TestResponseMixin::assertGraphQLErrorMessage\(\) invoked with 1 parameter, 0 required\.#' - path: src/Testing/TestResponseMixin.php @@ -42,6 +42,9 @@ parameters: - path: src/Testing/TestResponseMixin.php message: '#Anonymous function should return Illuminate\\Testing\\TestResponse but returns .*#' + # Recognition of mixin methods is broken in some PHPStan/Larastan versions + - '#Call to an undefined method Illuminate\\Testing\\TestResponse::(assertGraphQLValidationError|assertGraphQLValidationKeys|assertGraphQLValidationPasses|assertGraphQLError|assertGraphQLErrorMessage|assertGraphQLDebugMessage|assertGraphQLErrorFree|assertGraphQLSubscriptionAuthorized|assertGraphQLSubscriptionNotAuthorized|graphQLSubscriptionMock|graphQLSubscriptionChannelName|assertGraphQLBroadcasted|assertGraphQLNotBroadcasted)\(\)\.#' + # Relation forwards calls to Builder - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder|Illuminate\\Database\\Eloquent\\Relations\\Relation|Illuminate\\Database\\Query\\Builder::(orderBy|where|whereIn|whereNotIn|whereBetween|whereJsonContains|whereNotBetween)\(\)\.#' diff --git a/src/Testing/TestResponseMixin.php b/src/Testing/TestResponseMixin.php index 8be0f9e330..fc296766f3 100644 --- a/src/Testing/TestResponseMixin.php +++ b/src/Testing/TestResponseMixin.php @@ -90,7 +90,7 @@ public function assertGraphQLErrorMessage(): \Closure }; } - public function assertGraphQLDebugMessage(): \Closure + public function assertGraphQLDebugMessage(): \Closure { return function (string $message): TestResponse { $messages = $this->json('errors.*.extensions.debugMessage'); From a93be3c967dea84e2ba1088cbf8125680935276b Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 11:49:06 +0100 Subject: [PATCH 06/19] ignore other errors --- benchmarks/QueryBench.php | 2 +- src/Console/FieldCommand.php | 3 +++ src/Console/LighthouseGeneratorCommand.php | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/benchmarks/QueryBench.php b/benchmarks/QueryBench.php index f3b88c1233..74e9e4667f 100644 --- a/benchmarks/QueryBench.php +++ b/benchmarks/QueryBench.php @@ -11,7 +11,7 @@ abstract class QueryBench extends TestCase /** Cached graphQL endpoint. */ protected string $graphQLEndpoint; - public function __construct() + public function __construct() // @phpstan-ignore method.parentMethodFinalByPhpDoc (yeah this is hacky) { parent::__construct(static::class); } diff --git a/src/Console/FieldCommand.php b/src/Console/FieldCommand.php index b25b457cef..58e0bd8159 100644 --- a/src/Console/FieldCommand.php +++ b/src/Console/FieldCommand.php @@ -37,6 +37,9 @@ protected function getDefaultNamespace($rootNamespace): string protected function nameParts(): array { $name = $this->argument('name'); + if (! is_string($name)) { + throw new \InvalidArgumentException('You must specify the name for the class to generate.'); + } $parts = explode('.', $name); if (count($parts) !== 2) { diff --git a/src/Console/LighthouseGeneratorCommand.php b/src/Console/LighthouseGeneratorCommand.php index f8c0f2f5b2..edd2527a40 100644 --- a/src/Console/LighthouseGeneratorCommand.php +++ b/src/Console/LighthouseGeneratorCommand.php @@ -15,7 +15,10 @@ abstract class LighthouseGeneratorCommand extends GeneratorCommand */ protected function getNameInput(): string { - $name = (string) $this->argument('name'); + $name = $this->argument('name'); + if (! is_string($name)) { + throw new \InvalidArgumentException('You must specify the name for the class to generate.'); + } return ucfirst(trim($name)); } From 4bb7001318779a5e7804ad62e78c2ed5dc205072 Mon Sep 17 00:00:00 2001 From: spawnia Date: Thu, 20 Mar 2025 10:49:58 +0000 Subject: [PATCH 07/19] Apply php-cs-fixer changes --- src/Testing/TestResponseMixin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Testing/TestResponseMixin.php b/src/Testing/TestResponseMixin.php index fc296766f3..8be0f9e330 100644 --- a/src/Testing/TestResponseMixin.php +++ b/src/Testing/TestResponseMixin.php @@ -90,7 +90,7 @@ public function assertGraphQLErrorMessage(): \Closure }; } - public function assertGraphQLDebugMessage(): \Closure + public function assertGraphQLDebugMessage(): \Closure { return function (string $message): TestResponse { $messages = $this->json('errors.*.extensions.debugMessage'); From c7b53a64a368ced1fc9f8a9a76f98a3fe3dac176 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 11:53:41 +0100 Subject: [PATCH 08/19] Clarify CacheConfig type --- phpstan.neon | 3 +-- src/Schema/AST/ASTCache.php | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index c1fae4f9a9..6e6b3cb81e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -51,8 +51,7 @@ parameters: # Laravel 11 changes a lot of generics - '#generic class (Illuminate\\Database\\Eloquent\\Builder|Laravel\\Scout\\Builder)( but)? does not specify its types#' - '#contains generic type Illuminate\\Testing\\TestResponse<.+> but class Illuminate\\Testing\\TestResponse is not generic\.#' - - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|MorphOne|MorphMany|MorphToMany)<.+, .+> in PHPDoc tag @return specifies 2 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' - - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(BelongsToMany)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 2: TRelatedModel, TDeclaringModel#' + - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<.+> in PHPDoc tag @return specifies \d template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only \d: .+#' - '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(HasOne|HasMany|BelongsToMany|MorphOne|MorphMany|MorphToMany)<(.+), .+> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2>.#' - '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(BelongsTo|MorphTo)<(.+), \$this\((.+)\)> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2, \3>.#' - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' diff --git a/src/Schema/AST/ASTCache.php b/src/Schema/AST/ASTCache.php index 867f877f1b..12ac3ba37a 100644 --- a/src/Schema/AST/ASTCache.php +++ b/src/Schema/AST/ASTCache.php @@ -9,8 +9,8 @@ /** * @phpstan-type CacheConfig array{ - * enable: bool, - * path: string|null, + * enable: bool|int, + * path?: ?string, * } * * @phpstan-import-type SerializableDocumentAST from DocumentAST From 073902e49266fce74c1faf62c3ef39007d162220 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 12:18:34 +0100 Subject: [PATCH 09/19] disable cacheResult to fix warning in coverage tests https://github.com/nuwave/lighthouse/actions/runs/13967607099/job/39101515130?pr=2665 --- phpunit.xml.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 05b78d8866..dd38db91ed 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,6 +2,7 @@ From 6e42948fbcfaee356ae97a09d4d42212a65d6268 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 14:12:15 +0100 Subject: [PATCH 10/19] failOnWarning="false" --- phpunit.xml.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index dd38db91ed..3309c185d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,8 +2,8 @@ + colors="true" + failOnWarning="false"> src From fe9548add06718f13fc4edfcba9e20958f957bab Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Thu, 20 Mar 2025 14:39:43 +0100 Subject: [PATCH 11/19] https://github.com/sebastianbergmann/phpunit/issues/6150 --- .gitignore | 2 +- phpunit.xml.dist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4da3699244..25e9e03dab 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ phpunit.xml .gitpod.yml # Generated files -.phpunit.result.cache +.phpunit.cache .php-cs-fixer.cache build phpstan-tmp-dir diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3309c185d4..e27e8e5bcb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -3,7 +3,7 @@ xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" - failOnWarning="false"> + cacheDirectory=".phpunit.cache"> src From 08d0bb989c3f5b37d30747ecc1665201f2993e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Gaulin?= Date: Tue, 25 Mar 2025 12:24:10 +0400 Subject: [PATCH 12/19] Fix PHPStan error Warning: Trying to access array offset on null src/Schema/AST/ASTCache.php:28 --- .phpstan-bootstrap.php | 9 +++++++++ phpstan.neon | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 .phpstan-bootstrap.php diff --git a/.phpstan-bootstrap.php b/.phpstan-bootstrap.php new file mode 100644 index 0000000000..af43b384e3 --- /dev/null +++ b/.phpstan-bootstrap.php @@ -0,0 +1,9 @@ +make('config'); +// Required by \Nuwave\Lighthouse\Schema\AST constructor when PHPStan analyzes code +$existingConfig->set('lighthouse.schema_cache.enable', false); diff --git a/phpstan.neon b/phpstan.neon index 6e6b3cb81e..e8da1d3b08 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,7 @@ parameters: level: 8 # TODO level up to max + bootstrapFiles: + - .phpstan-bootstrap.php stubFiles: - _ide_helper.php paths: From eef2e6c7707ab6e4d689ad7f6a74befd1075901b Mon Sep 17 00:00:00 2001 From: spawnia Date: Tue, 25 Mar 2025 09:02:46 +0000 Subject: [PATCH 13/19] Apply php-cs-fixer changes --- .phpstan-bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.phpstan-bootstrap.php b/.phpstan-bootstrap.php index af43b384e3..5e77c4753d 100644 --- a/.phpstan-bootstrap.php +++ b/.phpstan-bootstrap.php @@ -1,4 +1,4 @@ - Date: Tue, 25 Mar 2025 15:41:21 +0100 Subject: [PATCH 14/19] revert PHPUnit cache dir --- .gitignore | 2 +- phpunit.xml.dist | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 25e9e03dab..4da3699244 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ phpunit.xml .gitpod.yml # Generated files -.phpunit.cache +.phpunit.result.cache .php-cs-fixer.cache build phpstan-tmp-dir diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e27e8e5bcb..05b78d8866 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,8 +2,7 @@ + colors="true"> src From 1a6522b2813637f6f2e5147b76557cc45d0d6955 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Mar 2025 15:49:20 +0100 Subject: [PATCH 15/19] iterable with int keys --- tests/Unit/Subscriptions/Storage/RedisStorageManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Subscriptions/Storage/RedisStorageManagerTest.php b/tests/Unit/Subscriptions/Storage/RedisStorageManagerTest.php index b76653f156..f0a5ed0e8f 100644 --- a/tests/Unit/Subscriptions/Storage/RedisStorageManagerTest.php +++ b/tests/Unit/Subscriptions/Storage/RedisStorageManagerTest.php @@ -25,7 +25,7 @@ final class RedisStorageManagerTest extends TestCase * @param array $firstCallArguments * @param array ...$consecutiveCallsArguments * - * @return iterable<\PHPUnit\Framework\Constraint\Callback> + * @return iterable> */ private function withConsecutive(array $firstCallArguments, array ...$consecutiveCallsArguments): iterable { From ceb8edb105e9de3e65e0702b14cd0ac5c9d3a8d2 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Mar 2025 15:58:55 +0100 Subject: [PATCH 16/19] fix PHPStan --- phpstan.neon | 3 +++ src/GraphQL.php | 6 +++--- src/Schema/Directives/RelationDirectiveHelpers.php | 2 +- tests/Unit/Schema/Directives/ComplexityDirectiveTest.php | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e8da1d3b08..7b09e9ea3b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -59,6 +59,9 @@ parameters: - '#Generic type Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<.+, .+, .+> in PHPDoc tag @return specifies 3 template types, but class Illuminate\\Database\\Eloquent\\Relations\\\1 supports only 1: TRelatedModel#' - '#Method .+ should return Illuminate\\Database\\Eloquent\\Relations\\(HasOneThrough|HasManyThrough)<(.+), .+> but returns Illuminate\\Database\\Eloquent\\Relations\\\1<\2>.#' + # Different between PHPUnit versions + - '#PHPDoc tag @return contains generic type PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker but class PHPUnit\\Framework\\MockObject\\Builder\\InvocationMocker is not generic.#' + # This test cheats and uses reflection to make assertions - path: tests/Unit/Schema/Directives/BaseDirectiveTest.php message: '#Call to protected method getModelClass\(\) of class Nuwave\\Lighthouse\\Schema\\Directives\\BaseDirective\.#' diff --git a/src/GraphQL.php b/src/GraphQL.php index b5938791c2..e2e1549543 100644 --- a/src/GraphQL.php +++ b/src/GraphQL.php @@ -161,9 +161,9 @@ public function executeParsedQueryRaw( $queryComplexityRule = $validationRules[QueryComplexity::class] ?? null; $queryComplexity = $queryComplexityRule instanceof QueryComplexity // TODO remove this check when updating the required version of webonyx/graphql-php - && method_exists($queryComplexityRule, 'getQueryComplexity') - ? $queryComplexityRule->getQueryComplexity() - : null; + && method_exists($queryComplexityRule, 'getQueryComplexity') // @phpstan-ignore function.alreadyNarrowedType (depends on the used library version) + ? $queryComplexityRule->getQueryComplexity() + : null; /** @var array<\Nuwave\Lighthouse\Execution\ExtensionsResponse|null> $extensionsResponses */ $extensionsResponses = (array) $this->eventDispatcher->dispatch( diff --git a/src/Schema/Directives/RelationDirectiveHelpers.php b/src/Schema/Directives/RelationDirectiveHelpers.php index d541f4f752..b86d0df16e 100644 --- a/src/Schema/Directives/RelationDirectiveHelpers.php +++ b/src/Schema/Directives/RelationDirectiveHelpers.php @@ -28,7 +28,7 @@ protected function relation(): string /** * @param array $args * - * @return \Closure(QueryBuilder|\Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model>, ?mixed = null): void + * @return \Closure(\Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder<\Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Relations\Relation<\Illuminate\Database\Eloquent\Model>, mixed=): void */ protected function makeBuilderDecorator(mixed $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): \Closure { diff --git a/tests/Unit/Schema/Directives/ComplexityDirectiveTest.php b/tests/Unit/Schema/Directives/ComplexityDirectiveTest.php index 418349a44d..b1a13f8b34 100644 --- a/tests/Unit/Schema/Directives/ComplexityDirectiveTest.php +++ b/tests/Unit/Schema/Directives/ComplexityDirectiveTest.php @@ -47,7 +47,7 @@ public function testDefaultComplexity(): void $this->assertCount(1, $events); // TODO remove this check when updating the required version of webonyx/graphql-php - if (method_exists(QueryComplexity::class, 'getQueryComplexity')) { + if (method_exists(QueryComplexity::class, 'getQueryComplexity')) { // @phpstan-ignore function.alreadyNarrowedType (depends on the used library version) $event = $events[0]; $this->assertSame(2, $event->queryComplexity); } From 88a2622356f9e796c32df0cf9c060f939801e753 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Mar 2025 16:27:35 +0100 Subject: [PATCH 17/19] Clean up PHPStan bootstrap --- .phpstan-bootstrap.php => phpstan-bootstrap.php | 5 +---- phpstan.neon | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) rename .phpstan-bootstrap.php => phpstan-bootstrap.php (50%) diff --git a/.phpstan-bootstrap.php b/phpstan-bootstrap.php similarity index 50% rename from .phpstan-bootstrap.php rename to phpstan-bootstrap.php index 5e77c4753d..37bc24908e 100644 --- a/.phpstan-bootstrap.php +++ b/phpstan-bootstrap.php @@ -2,8 +2,5 @@ require_once __DIR__ . '/vendor/autoload.php'; -use Illuminate\Container\Container; - -$existingConfig = Container::getInstance()->make('config'); // Required by \Nuwave\Lighthouse\Schema\AST constructor when PHPStan analyzes code -$existingConfig->set('lighthouse.schema_cache.enable', false); +config(['lighthouse.schema_cache.enable' => false]); diff --git a/phpstan.neon b/phpstan.neon index 7b09e9ea3b..894fb6f3b8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ parameters: level: 8 # TODO level up to max bootstrapFiles: - - .phpstan-bootstrap.php + - phpstan-bootstrap.php stubFiles: - _ide_helper.php paths: From da2eca9e41075ae2c16349357b1c81f32e2a9842 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Mar 2025 16:32:14 +0100 Subject: [PATCH 18/19] fix more PHPStan --- phpstan.neon | 3 +++ src/GraphQL.php | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 894fb6f3b8..e92c8135b2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -69,6 +69,9 @@ parameters: # This is a library, so it should be extendable - '#Unsafe usage of new static.*#' + # Possible footgun, but people who extend Lighthouse classes should know what they are doing + - '#Cannot unset property .+ because it might have hooks in a subclass.' + # Ease transition for non-nullable properties towards native types https://github.com/phpstan/phpstan/issues/5150 - '#Property .* in isset\(\) is not nullable\.#' diff --git a/src/GraphQL.php b/src/GraphQL.php index e2e1549543..1cf90c1ebc 100644 --- a/src/GraphQL.php +++ b/src/GraphQL.php @@ -404,13 +404,13 @@ protected function validateCacheableRules( } if ($queryHash === null) { - return DocumentValidator::validate($schema, $query, $validationRules); + return DocumentValidator::validate($schema, $query, $validationRules); // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php) } $cacheConfig = $this->configRepository->get('lighthouse.validation_cache'); if (! isset($cacheConfig['enable']) || ! $cacheConfig['enable']) { - return DocumentValidator::validate($schema, $query, $validationRules); + return DocumentValidator::validate($schema, $query, $validationRules); // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php) } $cacheKey = "lighthouse:validation:{$schemaHash}:{$queryHash}"; @@ -429,7 +429,7 @@ protected function validateCacheableRules( // As of webonyx/graphql-php 15.14.0, GraphQL\Error\Error is not serializable. // We would have to figure out how to serialize them properly to cache them. if ($result !== []) { - return $result; + return $result; // @phpstan-ignore return.type (TODO remove ignore when requiring a newer version of webonyx/graphql-php) } $store->put($cacheKey, $result, $cacheConfig['ttl']); From c5fb8bbd9a8cfe9b53634a5d4c4176d422bfaac0 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Mar 2025 16:33:26 +0100 Subject: [PATCH 19/19] fix ignore --- phpstan.neon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index e92c8135b2..028c1471f2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -70,7 +70,7 @@ parameters: - '#Unsafe usage of new static.*#' # Possible footgun, but people who extend Lighthouse classes should know what they are doing - - '#Cannot unset property .+ because it might have hooks in a subclass.' + - '#Cannot unset property .+ because it might have hooks in a subclass\.#' # Ease transition for non-nullable properties towards native types https://github.com/phpstan/phpstan/issues/5150 - '#Property .* in isset\(\) is not nullable\.#'