rfc:no_serialize_attribute

PHP RFC: Add #[NoSerialize] attribute for excluding properties from serialization

Introduction

Serialization is a fundamental mechanism in PHP that allows objects to be converted into a storable or transferable representation.

However, not every property of an object should necessarily be serialized. Frameworks and libraries often contain transient or resource-based properties—such as database connections, file handles, or caches—that should not be persisted.

Currently, developers must manually handle this by overriding __sleep() or __serialize(), which can lead to repetitive boilerplate and maintenance overhead.

Proposal

This proposal introduces a new #[NoSerialize] attribute that can be applied to class properties to exclude them from native PHP serialization explicitly.

It provides a declarative alternative to manually filtering properties within __sleep() or __serialize(), making serialization rules easier to maintain and more self-documenting.

1. Syntax and Definition

<?php
 
use Attribute;
 
/**
 * Marks a property as excluded from native serialization.
 *
 * When applied, this property will not appear in the output
 * of serialize(), nor be restored during unserialize().
 */
#[Attribute(Attribute::TARGET_PROPERTY)]
final class NoSerialize {}
 
?>

Usage example:

<?php
 
class Example
{
public string $name;
 
    #[NoSerialize]
    public PDO $connection;
}
 
$object = new Example();
$object->name = "User";
$object->connection = new PDO('sqlite::memory:');
 
echo serialize($object);
// Serialized output will be `O:7:"Example":1:{s:4:"name";s:4:"User";}`.
?>

2. Semantics

2.1 Behavior in Native Serialization

  • When serialize() is invoked and the class does not define its own __serialize() or __sleep(), properties marked with #[NoSerialize] are skipped automatically.
  • The resulting serialized data omits these properties entirely.
  • Upon unserialize(), all properties present in the serialized payload are restored normally.
  • The #[NoSerialize] attribute does not affect deserialization — if a property exists in the serialized data (e.g., produced by an older version of the class or by userland code), it will be deserialized.
  • Skipped properties (those not present in the data) remain uninitialized if they have no default, or are restored to their declared default value.
class SessionWrapper
{
    public string $id;
 
    #[NoSerialize]
    public $resource; // transient field
 
    public function __construct()
    {
        $this->resource = fopen('php://memory', 'r+');
    }
}
 
$s = new SessionWrapper();
$s->id = 'abc';
 
var_dump(unserialize(serialize($s)));
/*
object(SessionWrapper)#2 (1) {
  ["id"]=>
  string(3) "abc"
}
*/

2.2 Interaction with __serialize() and __sleep()

If a class defines its own serialization logic via __serialize() or __sleep(), the #[NoSerialize] attribute has no effect.

These methods are entirely user-defined, and PHP does not automatically filter out attributes marked with #[NoSerialize]. This design maintains explicit and consistent behavior with existing PHP semantics — developer code always takes precedence when custom serialization is implemented.

Example:

class Custom
{
    public string $a = 'A';
 
    #[NoSerialize]
    public string $b = 'B';
 
    public function __serialize(): array
    {
        return ['a' => $this->a, 'b' => $this->b];
    }
}
 
echo serialize(new Custom());
// Output still contains both 'a' and 'b'

Developers who wish to honor #[NoSerialize] within a custom __serialize() can do so manually using reflection:

class Custom
{
    public string $a = 'A';
    #[NoSerialize]
    public string $b = 'B';
 
    public function __serialize(): array
    {
        $result = [];
        foreach ((new ReflectionObject($this))->getProperties() as $prop) {
            if (!$prop->getAttributes(NoSerialize::class)) {
                $prop->setAccessible(true);
                $result[$prop->getName()] = $prop->getValue($this);
            }
        }
        return $result;
    }
}

2.3 Inheritance and Traits

  • #[NoSerialize] applies only to the declaring property and is not inherited when a child class redeclares the same property.
  • Properties introduced via traits preserve the attribute when the trait is used.
  • Promoted constructor properties can include the attribute as usual:
class Example
{
    public function __construct(
        public string $name,
        #[NoSerialize]
        public ?PDO $db = null
    ) {}
}

2.4 Interaction with other serialization forms

2.5 Reflection API

The attribute is visible and queryable via reflection:

$rp = new ReflectionProperty(Example::class, 'connection');
var_dump($rp->getAttributes(NoSerialize::class)); // array(1) { ... }

2.6 Invalid Targets & Compile-Time Diagnostics

Applying #[NoSerialize] to static or virtual properties is not meaningful for native serialization. The engine will therefore emit compile-time warnings and ignore the attribute in these cases.

Engine messages (proposed):

  • Static property %s::$%s is not serializable
  • Virtual property %s::$%s is not serializable

Backward Incompatible Changes

NoSerialize can no longer be used as a class name in the global namespace. A GitHub search for “class NoSerialize” language:php revealed only one match in source code. The class is defined within a namespace.

Proposed PHP Version(s)

Next version of PHP (PHP 8.6 or PHP 9.0)

RFC Impact

To the Ecosystem

What effect will the RFC have on IDEs, Language Servers (LSPs), Static Analyzers, Auto-Formatters, Linters and commonly used userland PHP libraries?

To Existing Extensions

None

To SAPIs

None

Open Issues

Make sure there are no open issues when the vote starts!

Future Scope

  • #[NotSerializable] — a class-level attribute that forbids any instance serialization (throws an error).
  • Allow __sleep() to return null or no value, signaling the engine to fall back to the default serialization logic, which would then automatically respect #[NoSerialize].

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

rfc/no_serialize_attribute.txt · Last modified: by dkulyk