rfc:not_serializable_attribute

PHP RFC: #[\NotSerializable] attribute

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:

<?php
 
#[NotSerializable]
final class MyService
{
    // …
}

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

  1. Security hardening. Prevent accidental participation in object-serialization gadget chains and reduce risk of sensitive data leakage via PHP’s native serialization format.
  2. Explicit API intent. Many classes are not meaningful outside process memory (e.g., services, handles, runtime registries). The attribute communicates and enforces this.
  3. Parity with engine capabilities. The engine already supports a “not serializable” class flag; this RFC introduces a userland switch to set it.
  4. 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]:

<?php
 
#[Attribute(Attribute::TARGET_CLASS)]
final class NotSerializable {}

Semantics

If $obj is an instance of a class (or enum) marked #[NotSerializable], the engine throws:

  Serialization of 'ClassName' is not allowed.

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

<?php
 
#[NotSerializable]
final class TokenBucket {
    public function __construct(
        private int $capacity,
        private float $refillRatePerSecond,
    ) {}
}
 
serialize(new TokenBucket(10, 1.5)); //  Exception: Serialization of 'TokenBucket' is not allowed
 
?>

Inheritance:

<?php
 
#[NotSerializable]
class Service {}
 
class SpecializedService extends Service {}
 
serialize(new SpecializedService()); // Exception: Serialization of 'SpecializedService' is not allowed

Enums:

<?php
 
#[NotSerializable]
enum HandleState: string {
    case Open = 'open';
    case Closed = 'closed';
}
serialize(HandleState::Open); // Exception: Serialization of 'HandleState' is not allowed

==== Interface:

<?php
 
#[NotSerializable]
interface IDBService {}
//Fatal error: Cannot apply #[\NotSerializable] to interface IDBServic
 
?>

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 the php/policies repository for the current voting guidelines.

Implement $feature as outlined in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

Implementation

After the RFC is implemented, this section should contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. 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.

rfc/not_serializable_attribute.txt · Last modified: by dkulyk