From 6e2246210a58be42ab32ee3ac9e6349df7256a3d Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 29 Jul 2026 10:27:42 +0200 Subject: [PATCH 1/5] Remove default values from public properties always assigned in the constructor Rector's RemoveDefaultValueFromAssignedPropertyRector reports these as redundant, because every constructor provably assigns them. The declarations are still observable through paths that bypass the constructor - unserialize(), newInstanceWithoutConstructor(), and subclasses that skip parent::__construct(). Those go from reading null or [] to a typed property initialization error, so this waits for v16. --- src/Executor/ExecutionResult.php | 6 +++--- src/Language/AST/Location.php | 6 +++--- src/Type/Definition/ResolveInfo.php | 4 ++-- src/Type/Schema.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Executor/ExecutionResult.php b/src/Executor/ExecutionResult.php index cc5ad1b44..ecb19c772 100644 --- a/src/Executor/ExecutionResult.php +++ b/src/Executor/ExecutionResult.php @@ -40,7 +40,7 @@ class ExecutionResult implements \JsonSerializable * * @var array|null */ - public ?array $data = null; + public ?array $data; /** * Errors registered during query execution. @@ -52,7 +52,7 @@ class ExecutionResult implements \JsonSerializable * * @var list */ - public array $errors = []; + public array $errors; /** * User-defined serializable array of extensions included in serialized result. @@ -61,7 +61,7 @@ class ExecutionResult implements \JsonSerializable * * @var array|null */ - public ?array $extensions = null; + public ?array $extensions; /** * @var callable|null diff --git a/src/Language/AST/Location.php b/src/Language/AST/Location.php index ff816bb52..1566c1d91 100644 --- a/src/Language/AST/Location.php +++ b/src/Language/AST/Location.php @@ -20,13 +20,13 @@ class Location public int $end; /** The Token at which this Node begins. */ - public ?Token $startToken = null; + public ?Token $startToken; /** The Token at which this Node ends. */ - public ?Token $endToken = null; + public ?Token $endToken; /** The Source document the AST represents. */ - public ?Source $source = null; + public ?Source $source; public static function create(int $start, int $end): self { diff --git a/src/Type/Definition/ResolveInfo.php b/src/Type/Definition/ResolveInfo.php index 5204cd547..a1615e0f1 100644 --- a/src/Type/Definition/ResolveInfo.php +++ b/src/Type/Definition/ResolveInfo.php @@ -98,7 +98,7 @@ class ResolveInfo * * @var array */ - public array $fragments = []; + public array $fragments; /** * Root value passed to query execution. @@ -123,7 +123,7 @@ class ResolveInfo * * @var array */ - public array $variableValues = []; + public array $variableValues; /** * @param \ArrayObject $fieldNodes diff --git a/src/Type/Schema.php b/src/Type/Schema.php index 46bebbe68..aae3275a5 100644 --- a/src/Type/Schema.php +++ b/src/Type/Schema.php @@ -78,7 +78,7 @@ class Schema public ?SchemaDefinitionNode $astNode; /** @var array */ - public array $extensionASTNodes = []; + public array $extensionASTNodes; /** * @param SchemaConfig|array $config From 213ca5a8654edc7a372694e6c5a64bb3ad55b58a Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 29 Jul 2026 10:31:45 +0200 Subject: [PATCH 2/5] Document squash merge preference and the breaking change label --- .ai/AGENTS.md | 3 ++- CONTRIBUTING.md | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.ai/AGENTS.md b/.ai/AGENTS.md index a7ced06e1..6e1daad51 100644 --- a/.ai/AGENTS.md +++ b/.ai/AGENTS.md @@ -42,7 +42,7 @@ The following elements are part of the stable public API: After merging a PR and releasing: -1. Merge the PR via `gh pr merge` (use `--merge` for a merge commit) +1. Merge the PR via `gh pr merge --squash`, or `--rebase` when the commit history is already clean (merge commits are disabled) 2. Pull latest master: `git pull` 3. Update CHANGELOG.md: move the entry from `## Unreleased` into a new versioned section (e.g. `## v15.32.0`), add the PR URL as a reference, leave an empty `## Unreleased` at the top 4. Commit and push the CHANGELOG update to master @@ -52,3 +52,4 @@ After merging a PR and releasing: Version bump rules (semver): - New feature → minor bump (v15.31.x → v15.32.0) - Bug fix / change → patch bump (v15.31.x → v15.31.x+1) +- Breaking change → major bump: label the PR `breaking change` and leave it open for the next major release instead of merging diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bafe39055..da7bf9894 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,9 @@ If your contribution requires significant or breaking changes, or if you plan to we recommend you to [create an issue](https://github.com/webonyx/graphql-php/issues/new) with a brief proposal and discuss it with us first. +Breaking changes are collected for the next major release. +Maintainers mark such pull requests with the [`breaking change`](https://github.com/webonyx/graphql-php/labels/breaking%20change) label, which may keep them open for a while. + For smaller contributions use this workflow: 1. Fork the project. From 150b9492e004b9a4f3eb4e2f7e24da31a4ef0e9d Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 29 Jul 2026 10:31:56 +0200 Subject: [PATCH 3/5] Remove double self-assignment in the ReactPHP example --- .../04-async-php/reactphp/hello-world-http-server/graphql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/04-async-php/reactphp/hello-world-http-server/graphql.php b/examples/04-async-php/reactphp/hello-world-http-server/graphql.php index d7d6dac57..e93dee381 100644 --- a/examples/04-async-php/reactphp/hello-world-http-server/graphql.php +++ b/examples/04-async-php/reactphp/hello-world-http-server/graphql.php @@ -33,7 +33,7 @@ 'resolve' => function ($rootValue, array $args) { $deferred = new \React\Promise\Deferred(); $promise = $deferred->promise(); - $promise = $promise = $promise->then(static fn (): string => $rootValue['prefix'] . $args['message']); + $promise = $promise->then(static fn (): string => $rootValue['prefix'] . $args['message']); $deferred->resolve(null); return $promise; From 004cecf4a6c846acc537bca13657db80a8963991 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 29 Jul 2026 10:32:34 +0200 Subject: [PATCH 4/5] Skip rector rules that rewrite unrelated files RemoveDeadInstanceOfAssertRector removes an intentional assert() in InputObjectType and moves the adjacent @phpstan-ignore-line onto the closing brace, which then fails make stan. RemoveDefaultValueFromAssignedPropertyRector drops defaults from nine public properties, which is a breaking change deferred to v16. --- rector.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rector.php b/rector.php index aa1596998..c39fb9322 100644 --- a/rector.php +++ b/rector.php @@ -21,7 +21,9 @@ Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector::class => [ __DIR__ . '/src/Type/Definition/CustomScalarType.php', // Refines PHPDoc types ], + Rector\DeadCode\Rector\Property\RemoveDefaultValueFromAssignedPropertyRector::class, // TODO reintroduce in v16 https://github.com/webonyx/graphql-php/pull/1959 - dropping defaults from public properties breaks reading them when the constructor is bypassed Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector::class, // TODO reintroduce when https://github.com/rectorphp/rector-src/pull/4491 is released + Rector\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector::class, // Sometimes necessary to prove runtime behavior matches defined types Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector::class, // Sometimes nicer for symmetry Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector::class, // Prefer self:: Rector\PHPUnit\CodeQuality\Rector\FuncCall\AssertFuncCallToPHPUnitAssertRector::class => [ From 580ac7d7011d45dec6f9e3fe0cfb042973a052df Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Wed, 29 Jul 2026 10:33:41 +0200 Subject: [PATCH 5/5] Stop skipping RemoveDefaultValueFromAssignedPropertyRector --- CHANGELOG.md | 4 ++++ rector.php | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 940edd760..503d76810 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 +### Changed + +- Remove default values from public properties that are always assigned in the constructor https://github.com/webonyx/graphql-php/pull/1959 + ## v15.37.1 ### Fixed diff --git a/rector.php b/rector.php index c39fb9322..85a297a75 100644 --- a/rector.php +++ b/rector.php @@ -21,7 +21,6 @@ Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingConstructorRector::class => [ __DIR__ . '/src/Type/Definition/CustomScalarType.php', // Refines PHPDoc types ], - Rector\DeadCode\Rector\Property\RemoveDefaultValueFromAssignedPropertyRector::class, // TODO reintroduce in v16 https://github.com/webonyx/graphql-php/pull/1959 - dropping defaults from public properties breaks reading them when the constructor is bypassed Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector::class, // TODO reintroduce when https://github.com/rectorphp/rector-src/pull/4491 is released Rector\DeadCode\Rector\StmtsAwareInterface\RemoveDeadInstanceOfAssertRector::class, // Sometimes necessary to prove runtime behavior matches defined types Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector::class, // Sometimes nicer for symmetry