# RFC: NotSerializable attribute

- Status: Inactive
- Authors: Max Semenik, maxsem.wiki@gmail.com
- Date: 2023-11-26
- Version: 1.0
- Original: https://wiki.php.net/rfc/not_serializable
- Page: https://externals.io/rfc/not_serializable
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

## Discussions on the mailing list

- [\[RFC\]\[Discussion\] NotSerializable attribute](https://externals.io/message/121969.md) — started on 2023-12-09, 21 messages

---

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

```php
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](https://github.com/wikimedia/mediawiki-libs-ScopedCallback/blob/master/src/ScopedCallback.php)).

## Proposal

Introduce a new attribute that would expose this functionality to userspace.

```php
#[NotSerializable]
class MyClass
{
}
 
serialize(new MyClass()); // Exception: Serialization of 'MyClass' is not allowed
```

The non-serializable flag is inherited by descendants:

```php
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:

```php
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

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.
