Problem/Motivation
The GitLab CI pipeline for this project had never actually run — the template include pointed at templates/include.gitlab-ci.yml, which does not exist in project/gitlab_templates, so every pipeline died with "Unable to run pipeline ... 0 jobs, yaml invalid". That is fixed separately. The first real run surfaced 40 PHPStan findings nobody had seen, in two related families:
- 30 ×
Drupal\Core\DependencyInjection\DependencySerializationTrait does not support private properties. - 10 ×
Read-only properties are incompatible with Drupal\Core\DependencyInjection\DependencySerializationTrait when the trait is used by a parent class on PHP < 8.4.
Example: modules/file_gate_assurance/src/Plugin/GateMethod/Assurance.php:77 declares private AssuranceVerifierInterface $verifier; on a class extending SignedUrl, whose ancestry pulls in DependencySerializationTrait. There are 52 occurrences of private readonly across src/ and modules/.
Why this is worth fixing rather than ignoring
This is not a lint preference. Drupal serialises plugin and form objects — the Form API round-trips the form object between builds, and plugin instances are serialised in several caching paths. DependencySerializationTrait::__wakeup() restores services by reflecting over object properties, and it cannot reach private properties declared on a class when the trait is inherited from a parent. On deserialisation those properties stay uninitialised, which surfaces later as a typed-property access error rather than as anything pointing back here.
For a module whose job is access control on private files, a gate-method plugin that deserialises into a half-initialised state is a failure mode worth taking seriously.
The readonly half is narrower: PHP 8.4 relaxed the restriction, and Drupal 11.4 requires PHP 8.3, so the affected window is real but shrinking.
Proposed resolution
Change the affected declarations from private to protected. That is what makes them visible to the trait's reflection-based restore, and it is the conventional Drupal answer.
Worth doing deliberately rather than with a blanket find-and-replace:
- Only properties on classes that actually inherit
DependencySerializationTraitneed to change. PHPStan names them, so work from its report rather than from a grep forprivate. - Widening visibility on a published module is an API surface change for anyone subclassing. Worth a release note even though these are implementation details.
- Add a kernel test that serialises and unserialises at least one gate-method plugin and one form, asserting the injected services survive. Without that, this regresses the moment someone adds a new promoted property.
Explicitly not proposing a phpstan.neon.dist ignore for these. Unlike the PHPUnit-attribute findings on the previous-major job — a genuine cross-version artifact, since PHPUnit 9 has no Attributes namespace — these describe real runtime behaviour. Silencing them would leave the module asserting a cleanliness it does not have.
Remaining tasks
- Work through the PHPStan report; change the named properties to protected.
- Add serialise/unserialise coverage for a gate method and a form.
- Re-run the pipeline and confirm the phpstan job reports zero.
- Decide whether this warrants a patch release or rides the next minor.
User interface changes
None.
API changes
Property visibility widens from private to protected on affected classes. No signature or behaviour change intended.
Data model changes
None.
Comments
Comment #5
jmcerdaBoth families are fixed and released in 1.0.1.
The private properties went to protected across the gate-method plugins, with a serialize/unserialize round-trip test covering the restore. The readonly half took longer to see: widening those properties from private to protected is what made the second rule reportable, and it only fires below PHP 8.4 — so a local run on a newer PHP showed nothing. It surfaced on a sibling module whose CI analyses on 8.3 deliberately, and the same defect was then confirmed here.
Ten readonly properties across SettingsForm and file_gate_form's FileGateForm. That was a real fatal, not a lint finding: PHP 8.3 is inside the supported range (^11.4 || ^12, and Drupal 11.4's floor is 8.3), and Drupal unserializes cached form objects on every rebuild.
Verified against the 1.0.1 tag with phpVersion pinned to 80300:
DependencySerializationTrait findings at PHP 8.3 on tag 1.0.1: 0
[OK] No errors
The reason this reached a stable release is addressed in the same version: the project shipped no phpstan.neon.dist, so CI analysed it with the shared default configuration rather than the level and rule set the sibling modules pin. 1.0.1 adds one, plus a CI job that runs on PHP 8.3 rather than the newest available — analysing only on the newest PHP would leave the supported floor the version least checked, which is how this stayed invisible.
Comment #7
jmcerdaClosing as shipped (DependencySerializationTrait / readonly constructor properties fixed in released code).