PHP 8.3: unserialize(): Upgrade E_NOTICE errors to E_WARNING

Version8.3
TypeChange

PHP provides serialize and unserialize() functions to serialize any PHP value (strings, integers, objects, NULL, arrays, Enums, etc.) to a string representation, and recreate the PHP value from that string representation.

$data = ['apple', 'banana', 'orange'];
$serialized = serialize($data);
// "a:3:{i:0;s:5:"apple";i:1;s:6:"banana";i:2;s:6:"orange";}"

$restoredData = unserialize($serialized);
// ['apple', 'banana', 'orange']

Prior to PHP 8.3, passing an invalid string to the unserialize() function emitted PHP notices (E_NOTICE) in certain cases such as syntax errors in the serialized string. This was changed to emit warnings (E_WARNING) since PHP 8.3 and later. Further, certain error conditions of the serialize() function are changed to emit E_WARNING as well.

unserialize("invalid-string");
- PHP Notice:  unserialize(): Error at offset 0 of 14 bytes
+ PHP Warning:  unserialize(): Error at offset 0 of 14 bytes

Ideally, failing to unserialize a given string should be a hard-failure and should throw an Exception. However, to maintain backwards compatibility and ease the upgrade paths, the error level is increased in PHP 8.3, with future potential in upgrading it to throw exceptions.

Error condition inconsistency
Not all unserialize() failures were emitting E_NOTICE errors. For example, unserializing a string that exceeds the maximum depth limit (configured with unserialize_max_depth INI setting since PHP 7.4) already emits an E_WARNING. This case continues to issue E_WARNING errors, and has not changed.

Affected Error Conditions

The following three error conditions that previously emitted an E_NOTICE are changed to emit E_WARNING since PHP 8.3:

  • Syntax errors (sometimes caused by incorrect serialization handlers) in the passed string
    unserialize('invalid string');
    - PHP Notice:  unserialize(): Error at offset 0 of 12 bytes
    + PHP Warning:  unserialize(): Error at offset 0 of 12 bytes
  • Failures in the custom unserialize handlers using __unserialize magic method; e.g the __unserialize() method not returning any value
    class Test {
        public function __unserialize(array $data) { } // Does not return anything
    }
    - PHP Notice: unserialize(): Unexpected end of serialized data
    + PHP Warning: unserialize(): Unexpected end of serialized data
  • Returning the same variable twice from __sleep() magic method causing a name clash

    class Test {
      public $foo = 'test';
      public  function __sleep() {
        return array("foo", "foo"); // Same value returned twice
      }
    }
    
    serialize(new Test());
    - PHP Notice: serialize(): "foo" is returned from __sleep() multiple times
    + PHP Warning: serialize(): "foo" is returned from __sleep() multiple times

Backwards Compatibility Impact

In PHP 8.0, PHP's default error reporting level was changed to E_ALL. Unless the error_reporting value was changed in a custom INI file, this should not introduce any new errors apart from the change in the severity.

Custom error handlers that previously ignored E_NOTICE errors might encounter the new E_WARNING because of the change in the severity. Rather than adjusting the error handlers to ignore these warnings, it is highly recommended to evaluate the warning and remediate the invalid error condition.

One notable caveat is that the new serialize pattern introduced for PHP 8.1 Enums. Serialized Enums take E:... format, and cannot be unserialized in PHP versions prior to PHP 8.1.


RFC Discussion Implementation