====== 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 ([[https://github.com/wikimedia/mediawiki-libs-ScopedCallback/blob/master/src/ScopedCallback.php|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.