====== PHP RFC: Prevent instantiation and cloning of __PHP_Incomplete_Class ======
* Version: 0.9
* Date: 2026-03-02
* Author: Jordi Kroon, jordikroon@me.com
* Status: Draft
* Implementation: https://github.com/php/php-src/pull/21325
===== 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:
// 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:
// 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:
// 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.
* Yes
* No
* Abstain
===== Patches and Tests =====
https://github.com/php/php-src/pull/21325
===== 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.