====== 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.