# RFC: Prevent instantiation and cloning of __PHP_Incomplete_Class

- Status: Under discussion
- Authors: Jordi Kroon, jordikroon@me.com
- Date: 2026-03-03
- Version: 1.0
- Implementation: https://github.com/php/php-src/pull/21325
- Original: https://wiki.php.net/rfc/deprecate-incomplete-class-instantiation
- Page: https://externals.io/rfc/deprecate-incomplete-class-instantiation
- 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

## Votes

### Prevent instantiation and cloning of __PHP_Incomplete_Class? (closed)

Yes: 0 · No: 0 · Abstain: 0

## Discussions on the mailing list

- [\[RFC\] Prevent instantiation and cloning of __PHP_Incomplete_Class](https://externals.io/message/130246.md) — started on 2026-03-03, 1 messages
- [\[RFC\] Prevent instantiation and cloning of __PHP_Incomplete_Class](https://externals.io/message/130267.md) — started on 2026-03-07, 1 messages

---

## Introduction

This RFC proposes to prevent instantiation and cloning of the internal class `__PHP_Incomplete_Class`.

`__PHP_Incomplete_Class` is used as a placeholder when `unserialise` encounters an object whose original class definition is not available. It represents an incomplete deserialization state and is not intended to be constructed or duplicated directly.

## Proposal

The following changes are proposed:

- Prevent instantiation of `__PHP_Incomplete_Class` via `new`
- Prevent invoking its constructor
- Prevent cloning of `__PHP_Incomplete_Class` instances

Attempting to instantiate the class will throw an Error stating that instantiation of `__PHP_Incomplete_Class` is not allowed.

Attempting to clone an instance will throw an Error indicating that the object is not cloneable. `ReflectionClass::isCloneable` will report `false` for this class.

Objects created internally by unserialize when a class definition is missing remain fully supported and unchanged.

## Motivation

`__PHP_Incomplete_Class` models a failure scenario during deserialization. Allowing it to be instantiated or cloned creates an artificial state that does not reflect how incomplete objects arise in practice.

Incomplete objects should only originate from `unserialize` when the serialized payload references a class that is not loaded. PHP then creates an `__PHP_Incomplete_Class` instance and stores the original class name for diagnostic purposes.

This change also aligns with existing constraints: extending `__PHP_Incomplete_Class` is already not possible, and instantiation via reflection based creation paths that would normally bypass constructors is already not possible. Preventing direct construction and cloning completes the model by ensuring incomplete objects can only arise from their intended source.

## Backward Incompatible Changes

Code that directly instantiates or clones `__PHP_Incomplete_Class` will now fail as `__PHP_Incomplete_Class` is an internal class not intended for direct use. However, any such usage will need to be refactored to create incomplete objects through unserialization or other means that do not involve direct instantiation or cloning.

### Examples

Instantiating `__PHP_Incomplete_Class` directly will now throw an Error:

```php
// Fatal error: Uncaught Error: Instantiation of class __PHP_Incomplete_Class is not allowed
new __PHP_Incomplete_Class();
```

Cloning an instance of `__PHP_Incomplete_Class` will now throw an Error:

```php
// Fatal error: Uncaught Error: Trying to clone an uncloneable object of class __PHP_Incomplete_Class
$incomplete = unserialize('O:16:"NonExistentClass":0:{}');
$clone = clone($incomplete);
```

Tests that previously relied on direct instantiation can create an incomplete object through unserialize of an unknown class name instead:

```php
// This will create an instance of __PHP_Incomplete_Class without directly instantiating it
$incomplete = unserialize('O:16:"NonExistentClass":0:{}');
 
/*
 * object(__PHP_Incomplete_Class)#1 (1) {
 *  ["__PHP_Incomplete_Class_Name"]=>
 *  string(16) "NonExistentClass"
 * }
 */
```

## Proposed PHP Version(s)

This change is proposed for PHP 8.6.

## RFC Impact

### To existing extensions

No expected impact. `__PHP_Incomplete_Class` is an internal class and extensions should not rely on constructing or cloning it directly.

### To userland code

Only code that explicitly constructs or clones `__PHP_Incomplete_Class` is affected. Normal serialization and deserialization flows are unaffected.

### Performance

No measurable performance impact is expected. The change only adds guards to construction and cloning paths.

## Voting Choices

The vote starts on 2026-xx-xx, ends on 2026-xx-xx, and requires 2/3 majority to be accepted.

**Vote: Prevent instantiation and cloning of \_\_PHP\_Incomplete\_Class?** — Yes: 0 · No: 0 · Abstain: 0 (closed)

## Patches and Tests

<https://github.com/php/php-src/pull/21325>

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

- Mailing List Discussion: <https://news-web.php.net/php.internals/130246>

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