====== PHP RFC: #[\NotSerializable] attribute ====== * Version: 0.9 * Date: 2025-10-14 * Author: Dmytro Kulyk, lnkvisitor.ts@gmail.com * Status: Draft * Implementation: https://github.com/php/php-src/pull/20163 ===== Introduction ===== PHP already has a robust engine-level mechanism for forbidding serialization of certain types (e.g., closures, internal classes). This RFC proposes exposing that capability to userland with a simple, declarative attribute: When applied, attempts to serialize() (or unserialize() into) such a class will be rejected by the engine, without invoking __sleep(), __serialize(), or legacy Serializable. Note: json_encode()/JsonSerializable and var_export() are out of scope for this RFC and remain unaffected. ===== Motivation ===== - Security hardening. Prevent accidental participation in object-serialization gadget chains and reduce risk of sensitive data leakage via PHP’s native serialization format. - Explicit API intent. Many classes are not meaningful outside process memory (e.g., services, handles, runtime registries). The attribute communicates and enforces this. - Parity with engine capabilities. The engine already supports a “not serializable” class flag; this RFC introduces a userland switch to set it. - Lower maintenance. Today, projects emulate “non-serializable” by throwing in __serialize()/__sleep() or via the deprecated Serializable interface. The attribute is cleaner and future-proof. ===== Proposal ===== Introduce a built-in attribute #[\NotSerializable]: ==== Semantics ==== * On serialization serialize($obj): If $obj is an instance of a class (or enum) marked #[NotSerializable], the engine throws: Serialization of 'ClassName' is not allowed. * On unserialization unserialize($str): If the payload contains an instance of a #[NotSerializable] class, the engine throws: Unserialization of 'ClassName' is not allowed. * Existing allowed_classes behavior remains unchanged and is applied before this check. === Inheritance: === * The attribute is inherited by child classes and cannot be “overridden” (i.e., prohibition is sticky). * Applying #[NotSerializable] to a subclass when the parent already has it is a no-op (but allowed). === Interfaces & traits: === The attribute cannot be applied to interfaces or traits. Doing so is a compile-time error. === Reflection: === The attribute is discoverable via ReflectionClass::getAttributes(NotSerializable::class). No new reflection APIs are proposed. ==== Examples ==== ==== Basic usage ==== ==== Inheritance: ==== ==== Enums: ==== ==== Interface: ===== Backward Incompatible Changes ===== The NotSerializable class name can no longer be used in the global namespace. A GitHub search for “class NotSerializable ” language:php revealed a total of 28 matches in source code. Almost all of them are in the tests. ===== Proposed PHP Version(s) ===== PHP 8.6 ===== RFC Impact ===== ==== To the Ecosystem ==== No breaking changes, but provides new metadata that advanced tools can optionally use for safer analysis and diagnostics ==== To Existing Extensions ==== None. ==== To SAPIs ==== None ===== Open Issues ===== Make sure there are no open issues when the vote starts! ===== Future Scope ===== This section should outline areas that you are not planning to work on in the scope of this RFC, but that might be iterated upon in the future by yourself or another contributor. This helps with long-term planning and ensuring this RFC does not prevent future work. ===== Voting Choices ===== Pick a title that reflects the concrete choice people will vote on. Please consult [[https://github.com/php/policies/blob/main/feature-proposals.rst#voting|the php/policies repository]] for the current voting guidelines. * Yes * No * Abstain ===== Patches and Tests ===== https://github.com/php/php-src/pull/20163 ===== Implementation ===== After the RFC 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 ===== References ===== Links to external references, discussions, or RFCs. ===== Rejected Features ===== Keep this updated with features that were discussed on the mail lists. ===== Changelog ===== If there are major changes to the initial proposal, please include a short summary with a date or a link to the mailing list announcement here, as not everyone has access to the wikis' version history.