PHP RFC: #[NotSerializable]
- Version: 1.0
- Date: 2023-11-26
- Author: Max Semenik, maxsem.wiki@gmail.com
- Status: Under discussion
- First Published at: http://wiki.php.net/rfc/not_serializable
Introduction
Some classes aren't supposed to be serialized. Currently, while PHP internal classes have a nice way of preventing being serialized/unserialized, userspace doesn't. I'm proposing to make them equal.
Compare the internals simply slapping ce_flags |= ZEND_ACC_NOT_SERIALIZABLE
to userspace having to do something like this:
class MyClass { public function __sleep() // Wait, what its signature is supposed to be? Does it matter? { throw new Exception('This class must not be serialized'); } public function __wakeup() { throw new Exception('This class must not be unserialized'); } }
Not only is this method bulky, it's also less readable. It also lacks a way to indicate the intention to various code analysers so that they could detect attempts to serialize such classes.
Analysis
As of the time I'm writing this, there are 94 uses of @not-serializable
in php-src. Examples include:
- Closures
- Various connections like
PDO
, etc. - Reflection
What could userspace use this for?
- Wrappers for all the above. Imagine a PDO wrapper that creates connections on demand. If the connection hasn't been established yet, its serialization will succeed, which results in unpredictable behavior.
- Secret information that shouldn't be accidentally exfiltrated by being serialized.
- Security-sensitive classes that are unsafe to unserialize with arbitrary data (example in the wild).
Proposal
Introduce a new attribute that would expose this functionality to userspace.
#[NotSerializable] class MyClass { } serialize(new MyClass()); // Exception: Serialization of 'MyClass' is not allowed
The non-serializable flag is inherited by descendants:
class MyOtherClass extends MyClass { } serialize(new MyOtherClass()); // Exception: Serialization of 'MyOtherClass' is not allowed
The above requires no changes to the engine whatsoever, all functionality is already present - it merely gets exposed to userspace.
This feature will be exposed to reflection by the following additions to ReflectionClass:
public const int IS_NOT_SERIALIZABLE = ZEND_ACC_NOT_SERIALIZABLE; public function isSerializable(): bool {}
Backward Incompatible Changes
The only breaking change is the addition of a new non-namespaced class.
Proposed PHP Version(s)
8.4.
Open Issues
Make sure there are no open issues when the vote starts!
Proposed Voting Choices
Implement this RFC? (Yes/no, 2/3 approval required.)
Patches and Tests
* Proposed PR: https://github.com/php/php-src/pull/12788
Implementation
After the project is implemented, this section should contain
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
- a link to the language specification section (if any)
Rejected Features
Keep this updated with features that were discussed on the mail lists.