rfc:not_serializable

PHP RFC: #[NotSerializable]

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 (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

Implementation

After the project 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
  4. a link to the language specification section (if any)

Rejected Features

Keep this updated with features that were discussed on the mail lists.

rfc/not_serializable.txt · Last modified: 2023/12/10 12:31 by maxsem