Problem/Motivation
DependencySerializationTrait sets values on properties. As on PHP 8.1 these properties can be marked readonly. This can cause problems.
Steps to reproduce
Add a readonly service property to a form.
Proposed resolution
Do something like what is suggested on https://github.com/Ocramius/GeneratedHydrator/issues/656
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Issue fork drupal-3424344
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
joelpittet#3506858: Error: Cannot initialize readonly property Drupal\og\Plugin\Field\FieldWidget\OgComplex::$membershipManager We are running into this on OG, thanks for putting this issue together @alexpott
Comment #3
lslalbaiUsing Drupal\Core\DependencyInjection\DependencySerializationTrait should not make it impossible to have readonly properties that refer to services on a class or classed that inherit from it.
Recommended changes to Drupal\Core\DependencyInjection\DependencySerializationTrait:
(new \ReflectionProperty($this, $key))->setValue($this, $container->get($service_id));(new \ReflectionProperty($this, $key))->setValue($this, $entity_type_manager->getStorage($entity_type_id));Comment #4
jepster_> Add a readonly service property to a form.
Cannot reproduce an issue here. I've modified web/core/lib/Drupal/Core/Installer/Form/SiteConfigureForm.php, to have readonly properties in the constructor:
After flushing the cache, I could update the form at /admin/config/system/site-information without issues or errors in my logs. I could also prove that \Drupal\Core\DependencyInjection\DependencySerializationTrait was involved, since \Drupal\Core\Form\FormBase uses the trait.
Also I do not see, where services are modified via DependencySerializationTrait. The injected services are readonly by intent.
Comment #6
ericgsmith commentedStill active as far as I can see - have linked a few other issues when contrib has encountered this problem.
They are set in __wakeup - potentially with your test in #4 the form was not being serialized - I encountered this in #2975863: Add an exposed filter to the Taxonomy overview view page when a form was being submitted a second time and failed as dependencies were not set. Likely this needs updates to the steps to reproduce as you were right in just following that.
When I also found is the documentation in https://www.drupal.org/docs/drupal-apis/services-and-dependency-injectio... includes examples where readonly is used.
Comment #7
godotislateRe-populating readonly class properties in
__wakeup()results in fatal errors, whether set directly as$this->property = 'value';or using reflection(new \ReflectionProperty($this, 'property'))->setValue($this, 'value');. See https://3v4l.org/OPTLj and https://3v4l.org/3mViBIt is possible to set readonly properties in
__unserialize()though, which is probably another reason to do #3548971: Replace PHP soft-deprecated __sleep()/__wakeup() with __serialize()/__unserialize().See https://3v4l.org/G0WCa
Comment #10
andypostComment #11
ishani patel commentedI've updated the changes below and raised an MR.
Removed:
- __sleep() — mutated $this->_serviceIds / $this->_entityStorages as a side-effect during serialization.
- __wakeup() — direct cause of Cannot initialize readonly property fatal error.
Added:
- __serialize() — same logic as __sleep() but returns a full key => value array without mutating '$this'.
- __unserialize() — same re-injection logic as __wakeup() but PHP allows readonly property assignment here as it is treated as an initialization context (confirmed at https://3v4l.org/G0WCa).
- Both old methods are kept as deprecated stubs for BC, delegating to the new ones.
Kindly check and review it.
Thanks!