PHP 8.3

Backward Incompatible Changes

PHP Core

Programs that were very close to overflowing the call stack

Programs that were very close to overflowing the call stack may now throw an Error when using more than zend.max_allowed_stack_size-zend.reserved_stack_size bytes of stack (fiber.stack_size-zend.reserved_stack_size for fibers).

Executing proc_get_status() multiple times

Executing proc_get_status() multiple times will now always return the right value on POSIX systems. Previously, only the first call of the function returned the right value. Executing proc_close() after proc_get_status() will now also return the right exit code. Previously this would return -1. Internally, this works by caching the result on POSIX systems. If the previous behaviour is required, it is possible to check the "cached" key in the array returned by proc_get_status() to check whether the result was cached.

Zend Max Execution Timers

Zend Max Execution Timers is now enabled by default for ZTS builds on Linux.

Uses of traits with static properties

Uses of traits with static properties will now redeclare static properties inherited from the parent class. This will create a separate static property storage for the current class. This is analogous to adding the static property to the class directly without traits.

Assigning a negative index to an empty array

Assigning a negative index $n to an empty array will now make sure that the next index is $n+1 instead of 0.

Class constant visibility variance check

Class constant visibility variance is now correctly checked when inherited from interfaces.

WeakMap entries whose key maps to itself

WeakMap entries whose key maps to itself (possibly transitively) may now be removed during cycle collection if the key is not reachable except by iterating over the WeakMap (reachability via iteration is considered weak). Previously, such entries would never be automatically removed.


The DateTime extension has introduced Date extension specific exceptions and errors under the DateError and DateException hierarchies, instead of warnings and generic exceptions. This improves error handling, at the expense of having to check for errors and exceptions.


Calling DOMChildNode::after(), DOMChildNode::before(), DOMChildNode::replaceWith() on a node that has no parent is now a no-op instead of a hierarchy exception, which is the behaviour demanded by the DOM specification.

Using the DOMParentNode and DOMChildNode methods without a document now works instead of throwing a DOM_HIERARCHY_REQUEST_ERR DOMException. This is in line with the behaviour demanded by the DOM specification.

Calling DOMDocument::createAttributeNS() without specifying a prefix would incorrectly create a default namespace, placing the element inside the namespace instead of the attribute. This bug is now fixed.

DOMDocument::createAttributeNS() would previously incorrectly throw a DOM_NAMESPACE_ERRNAMESPACE_ERR DOMException when the prefix was already used for a different URI. It now correctly chooses a different prefix when there's a prefix name conflict.

New methods and properties were added to some DOM classes. If a userland class inherits from these classes and declare a method or property with the same name, the declarations must be compatible. Otherwise, a typical compile error about incompatible declarations will be thrown. See the list of new features and new functions for a list of the newly implemented methods and properties.


C functions that have a return type of void now return null instead of returning the following object object(FFI\CData:void) { }


The opcache.consistency_checks INI directive was removed. This feature was broken with the tracing JIT, as well as with inheritance cache, and has been disabled without a way to enable it since PHP 8.1.18 and PHP 8.2.5. Both the tracing JIT and inheritance cache may modify shm after the script has been persisted by invalidating its checksum. The attempted fix skipped over the modifiable pointers but was rejected due to complexity. For this reason, it was decided to remove the feature instead.


The type of Phar class constants are now declared.


The range() function has had various changes:

  • A TypeError is now thrown when passing objects, resources, or arrays as the boundary inputs.
  • A more descriptive ValueError is thrown when passing 0 for $step.
  • A ValueError is now thrown when using a negative $step for increasing ranges.
  • If $step is a float that can be interpreted as an int, it is now done so.
  • A ValueError is now thrown if any argument is infinity or NAN.
  • An E_WARNING is now emitted if $start or $end is the empty string. The value continues to be cast to the value 0.
  • An E_WARNING is now emitted if $start or $end has more than one byte, only if it is a non-numeric string.
  • An E_WARNING is now emitted if $start or $end is cast to an integer because the other boundary input is a number. (e.g. range(5, 'z');).
  • An E_WARNING is now emitted if $step is a float when trying to generate a range of characters, except if both boundary inputs are numeric strings (e.g. range('5', '9', 0.5); does not produce a warning).
  • range() now produce a list of characters if one of the boundary inputs is a string digit instead of casting the other input to int (e.g. range('9', 'A');).
range('9', 'A'); // ["9", ":", ";", "<", "=", ">", "?", "@", "A"], as of PHP 8.3.0
range('9', 'A'); // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], prior to PHP 8.3.0

The file() flags error check now catches all invalid flags. Notably FILE_APPEND was previously silently accepted.


The type of SNMP class constants are now declared.

PHP 8.2

Backward Incompatible Changes


DateTime::createFromImmutable() now has a tentative return type of static, previously it was DateTime.

DateTimeImmutable::createFromMutable() now has a tentative return type of static, previously it was DateTimeImmutable.

number symbols in relative formats no longer accept multiple signs, e.g. +-2.


The ODBC extension now escapes the username and password for the case when both a connection string and username/password are passed, and the string must be appended to. Before, user values containing values needing escaping could have created a malformed connection string, or injected values from user-provided data. The escaping rules should be identical to the .NET BCL DbConnectionOptions behaviour.


The PDO_ODBC extension also escapes the username and password when a connection string is passed. See the change to the ODBC extension for further details.


glob() now returns an empty array if all paths are restricted by open_basedir. Previously it returned false. Moreover, a warning is now emitted even if only some paths are restricted by open_basedir.

FilesystemIterator::__construct(): prior to PHP 8.2.0, FilesystemIterator::SKIP_DOTS constant was always set and couldn't be disabled. In order to maintain the previous behaviour the constant must be explicitly set when using the flags parameter. The default value from flags parameter has not been modified.

strtolower(), strtoupper(), stristr(), stripos(), strripos(), lcfirst(), ucfirst(), ucwords(), and str_ireplace() are no longer locale-sensitive. They now perform ASCII case conversion, as if the locale were "C". Localized versions of these functions are available in the MBString extension. Moreover, array_change_key_case(), and sorting with SORT_FLAG_CASE now also use ASCII case conversion.

str_split() returns an empty array for an empty string now. Previously it returned an array with a single empty string entry. mb_str_split() is not affected by this change as it was already behaving like that.

ksort() and krsort() now do numeric string comparison under SORT_REGULAR using the standard PHP 8 rules now.

var_export() no longer omits the leading backslash for exported classes, i.e. these are now fully qualified.

Standard PHP Library (SPL)

The following methods now enforce their signature:

SplFileObject::hasChildren() now has a tentative return type of false, previously it was bool.

SplFileObject::getChildren() now has a tentative return type of null, previously it was ?RecursiveIterator.

GlobIterator now returns an empty array if all paths are restricted by open_basedir. Previously it returned false. Moreover, a warning is now emitted even if only some paths are restricted by open_basedir.

PHP 8.1

Backward Incompatible Changes

PHP Core

$GLOBALS Access Restrictions

Access to the $GLOBALS array is now subject to a number of restrictions. Read and write access to individual array elements like $GLOBALS['var'] continues to work as-is. Read-only access to the entire $GLOBALS array also continues to be supported. However, write access to the entire $GLOBALS array is no longer supported. For example, array_pop($GLOBALS) will result in an error.

Usage of static Variables in Inherited Methods

When a method using static variables is inherited (but not overridden), the inherited method will now share static variables with the parent method.

class A {
public static function
counter() {
$counter = 0;
B extends A {}
var_dump(A::counter()); // int(1)
var_dump(A::counter()); // int(2)
var_dump(B::counter()); // int(3), previously int(1)
var_dump(B::counter()); // int(4), previously int(2)
This means that static variables in methods now behave the same way as static properties.

Optional parameters specified before required parameters

An optional parameter specified before required parameters is now always treated as required, even when called using named arguments. As of PHP 8.0.0, but prior to PHP 8.1.0, the below emits a deprecation notice on the definition, but runs successfully when called. As of PHP 8.1.0, an error of class ArgumentCountError is thrown, as it would be when called with positional arguments.

function makeyogurt($container = "bowl", $flavour)
"Making a $container of $flavour yogurt.\n";
makeyogurt(flavour: "raspberry");
catch (
Error $e)
get_class($e), ' - ', $e->getMessage(), "\n";

Output of the above example in PHP 8.0:

Deprecated: Required parameter $flavour follows optional parameter $container
 in example.php on line 3
Making a bowl of raspberry yogurt.

Output of the above example in PHP 8.1:

Deprecated: Optional parameter $container declared before required parameter
 $flavour is implicitly treated as a required parameter in example.php on line 3
ArgumentCountError - makeyogurt(): Argument #1 ($container) not passed

Note that a default value of null can be used before required parameters to specify a nullable type, but the parameter will still be required.

Return Type Compatibility with Internal Classes

Most non-final internal methods now require overriding methods to declare a compatible return type, otherwise a deprecated notice is emitted during inheritance validation. In case the return type cannot be declared for an overriding method due to PHP cross-version compatibility concerns, a ReturnTypeWillChange attribute can be added to silence the deprecation notice.

New Keywords

readonly is a keyword now. However, it still may be used as function name.

never is now a reserved word, so it cannot be used to name a class, interface or trait, and is also prohibited from being used in namespaces.

Resource to Object Migration

Several resources have been migrated to objects. Return value checks using is_resource() should be replaced with checks for false.


mysqli_fetch_fields(), and mysqli_fetch_field_direct() will now always return 0 for the max_length. This information can be computed by iterating over the result set, and taking the maximum length. This is what PHP was doing internally previously.

The MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH option no longer has any effect.

The MYSQLI_STORE_RESULT_COPY_DATA option no longer has any effect. Passing any value to the mode parameter of mysqli::store_result() no longer has any effect.

mysqli::connect() now returns true instead of null on success.

The default error handling mode has been changed from "silent" to "exceptions" See the MySQLi reporting mode page for more details on what this entails, and how to explicitly set this attribute. To restore the previous behaviour use: mysqli_report(MYSQLI_REPORT_OFF);

Classes extending mysqli_stmt::execute() are now required to specify the additional optional parameter.


The mysqlnd.fetch_data_copy INI directive has been removed. This should not result in user-visible behavior changes.


EC private keys will now be exported in PKCS#8 format rather than traditional format, just like all other keys.

openssl_pkcs7_encrypt() and openssl_cms_encrypt() will now to default using AES-128-CBC rather than RC2-40. The RC2-40 cipher is considered insecure and not enabled by default by OpenSSL 3.

PHP Data Objects

PDO::ATTR_STRINGIFY_FETCHES now stringifies values of type bool to "0" or "1". Previously bools were not stringified.

Calling PDOStatement::bindColumn() with PDO::PARAM_LOB will now constantly bind a stream result when PDO::ATTR_STRINGIFY_FETCHES is not enabled. Previously, the result would either be a stream or a string depending on the used database driver and the time the binding is performed.

MySQL Driver

Integers and floats in result sets will now be returned using native PHP types instead of strings when using emulated prepared statements. This matches the behavior of native prepared statements. The previous behaviour can be restored by enabling the PDO::ATTR_STRINGIFY_FETCHES option.

SQLite Driver

Integers and floats in results sets will now be returned using native PHP types. The previous behaviour can be restored by enabling the PDO::ATTR_STRINGIFY_FETCHES option.


To comply with the ArrayAccess interface, Phar::offsetUnset() and PharData::offsetUnset() no longer return a bool.


version_compare() no longer accepts undocumented operator abbreviations.

htmlspecialchars(), htmlentities(), htmlspecialchars_decode(), html_entity_decode(), and get_html_translation_table() now use ENT_QUOTES | ENT_SUBSTITUTE rather than ENT_COMPAT by default. This means that ' is escaped to ' while previously nothing was done. Additionally, malformed UTF-8 will be replaced by a Unicode substitution character, instead of resulting in an empty string.

debug_zval_dump() now prints the refcount of a reference wrappers with their refcount, instead of only prepending & to the value. This more accurately models reference representation since PHP 7.0.

debug_zval_dump() now prints interned instead of a dummy refcount for interned strings and immutable arrays.

Standard PHP Library (SPL)

SplFixedArray, will now be JSON encoded like an array

PHP 8.0

Backward Incompatible Changes

PHP Core

String to Number Comparison

Non-strict comparisons between numbers and non-numeric strings now work by casting the number to string and comparing the strings. Comparisons between numbers and numeric strings continue to work as before. Notably, this means that 0 == "not-a-number" is considered false now.

Comparison Before After
0 == "0" true true
0 == "0.0" true true
0 == "foo" true false
0 == "" true false
42 == " 42" true true
42 == "42foo" true false

Other incompatible Changes

  • match is now a reserved keyword.

  • mixed is now a reserved word, so it cannot be used to name a class, interface or trait, and is also prohibited from being used in namespaces.

  • Assertion failures now throw by default. If the old behavior is desired, assert.exception=0 can be set in the INI settings.

  • Methods with the same name as the class are no longer interpreted as constructors. The __construct() method should be used instead.

  • The ability to call non-static methods statically has been removed. Thus is_callable() will fail when checking for a non-static method with a classname (must check with an object instance).

  • The (real) and (unset) casts have been removed.

  • The track_errors ini directive has been removed. This means that php_errormsg is no longer available. The error_get_last() function may be used instead.

  • The ability to define case-insensitive constants has been removed. The third argument to define() may no longer be true.

  • The ability to specify an autoloader using an __autoload() function has been removed. spl_autoload_register() should be used instead.

  • The errcontext argument will no longer be passed to custom error handlers set with set_error_handler().

  • create_function() has been removed. Anonymous functions may be used instead.

  • each() has been removed. foreach or ArrayIterator should be used instead.

  • The ability to unbind this from closures that were created from a method, using Closure::fromCallable() or ReflectionMethod::getClosure(), has been removed.

  • The ability to unbind this from proper closures that contain uses of this has also been removed.

  • The ability to use array_key_exists() with objects has been removed. isset() or property_exists() may be used instead.

  • The behavior of array_key_exists() regarding the type of the key parameter has been made consistent with isset() and normal array access. All key types now use the usual coercions and array/object keys throw a TypeError.

  • Any array that has a number n as its first numeric key will use n+1 for its next implicit key, even if n is negative.

  • The default error_reporting level is now E_ALL. Previously it excluded E_NOTICE and E_DEPRECATED.

  • display_startup_errors is now enabled by default.

  • Using parent inside a class that has no parent will now result in a fatal compile-time error.

  • The @ operator will no longer silence fatal errors (E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_PARSE). Error handlers that expect error_reporting to be 0 when @ is used, should be adjusted to use a mask check instead:

    // Replace
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (
    error_reporting() == 0) {
    // ...

    // With
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (!(
    error_reporting() & $err_no)) {
    // ...

    Additionally, care should be taken that error messages are not displayed in production environments, which can result in information leaks. Please ensure that display_errors=Off is used in conjunction with error logging.

  • #[ is no longer interpreted as the start of a comment, as this syntax is now used for attributes.

  • Inheritance errors due to incompatible method signatures (LSP violations) will now always generate a fatal error. Previously a warning was generated in some cases.

  • The precedence of the concatenation operator has changed relative to bitshifts and addition as well as subtraction.

    echo "Sum: " . $a + $b;
    // was previously interpreted as:
    echo ("Sum: " . $a) + $b;
    // is now interpreted as:
    echo "Sum:" . ($a + $b);
  • Arguments with a default value that resolves to null at runtime will no longer implicitly mark the argument type as nullable. Either an explicit nullable type, or an explicit null default value has to be used instead.

    // Replace
    function test(int $arg = CONST_RESOLVING_TO_NULL) {}
    // With
    function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
    // Or
    function test(int $arg = null) {}
  • A number of warnings have been converted into Error exceptions:

    • Attempting to write to a property of a non-object. Previously this implicitly created an stdClass object for null, false and empty strings.
    • Attempting to append an element to an array for which the PHP_INT_MAX key is already used.
    • Attempting to use an invalid type (array or object) as an array key or string offset.
    • Attempting to write to an array index of a scalar value.
    • Attempting to unpack a non-array/Traversable.
    • Attempting to access unqualified constants which are undefined. Previously, unqualified constant accesses resulted in a warning and were interpreted as strings.
    • Passing the wrong number of arguments to a non-variadic built-in function will throw an ArgumentCountError.

    A number of notices have been converted into warnings:

    • Attempting to read an undefined variable.
    • Attempting to read an undefined property.
    • Attempting to read an undefined array key.
    • Attempting to read a property of a non-object.
    • Attempting to access an array index of a non-array.
    • Attempting to convert an array to string.
    • Attempting to use a resource as an array key.
    • Attempting to use null, a boolean, or a float as a string offset.
    • Attempting to read an out-of-bounds string offset.
    • Attempting to assign an empty string to a string offset.
  • Attempting to assign multiple bytes to a string offset will now emit a warning.

  • Unexpected characters in source files (such as NUL bytes outside of strings) will now result in a ParseError exception instead of a compile warning.

  • Uncaught exceptions now go through "clean shutdown", which means that destructors will be called after an uncaught exception.

  • The compile time fatal error "Only variables can be passed by reference" has been delayed until runtime, and converted into an "Argument cannot be passed by reference" Error exception.

  • Some "Only variables should be passed by reference" notices have been converted to "Argument cannot be passed by reference" exception.

  • The generated name for anonymous classes has changed. It will now include the name of the first parent or interface:

    new class extends ParentClass {};
    // -> ParentClass@anonymous
    new class implements FirstInterface, SecondInterface {};
    // -> FirstInterface@anonymous
    new class {};
    // -> class@anonymous

    The name shown above is still followed by a NUL byte and a unique suffix.

  • Non-absolute trait method references in trait alias adaptations are now required to be unambiguous:

    class X {
    T1, T2 {
    func as otherFunc;
    func() {}

    If both T1::func() and T2::func() exist, this code was previously silently accepted, and func was assumed to refer to T1::func. Now it will generate a fatal error instead, and either T1::func or T2::func needs to be written explicitly.

  • The signature of abstract methods defined in traits is now checked against the implementing class method:

    trait MyTrait {
    abstract private function
    neededByTrait(): string;

    MyClass {

    // Error, because of return type mismatch.
    private function neededByTrait(): int { return 42; }
  • Disabled functions are now treated exactly like non-existent functions. Calling a disabled function will report it as unknown, and redefining a disabled function is now possible.

  • data:// stream wrappers are no longer writable, which matches the documented behavior.

  • The arithmetic and bitwise operators +, -, *, /, **, %, <<, >>, &, |, ^, ~, ++, -- will now consistently throw a TypeError when one of the operands is an array, resource or non-overloaded object. The only exception to this is the array + array merge operation, which remains supported.

  • Float to string casting will now always behave locale-independently.

    setlocale(LC_ALL, "de_DE");
    $f = 3.14;
    $f, "\n";
    // Previously: 3,14
    // Now: 3.14

    See printf(), number_format() and NumberFormatter() for ways to customize number formatting.

  • Support for deprecated curly braces for offset access has been removed.

    // Instead of:
    // Write:
  • Applying the final modifier on a private method will now produce a warning unless that method is the constructor.

  • If an object constructor exit()s, the object destructor will no longer be called. This matches the behavior when the constructor throws.

  • Namespaced names can no longer contain whitespace: While Foo\Bar will be recognized as a namespaced name, Foo \ Bar will not. Conversely, reserved keywords are now permitted as namespace segments, which may also change the interpretation of code: new\x is now the same as constant('new\x'), not new \x().

  • Nested ternaries now require explicit parentheses.

  • debug_backtrace() and Exception::getTrace() will no longer provide references to arguments. It will not be possible to change function arguments through the backtrace.

  • Numeric string handling has been altered to be more intuitive and less error-prone. Trailing whitespace is now allowed in numeric strings for consistency with how leading whitespace is treated. This mostly affects:

    • The is_numeric() function
    • String-to-string comparisons
    • Type declarations
    • Increment and decrement operations

    The concept of a "leading-numeric string" has been mostly dropped; the cases where this remains exist in order to ease migration. Strings which emitted an E_NOTICE "A non well-formed numeric value encountered" will now emit an E_WARNING "A non-numeric value encountered" and all strings which emitted an E_WARNING "A non-numeric value encountered" will now throw a TypeError. This mostly affects:

    • Arithmetic operations
    • Bitwise operations

    This E_WARNING to TypeError change also affects the E_WARNING "Illegal string offset 'string'" for illegal string offsets. The behavior of explicit casts to int/float from strings has not been changed.

  • Magic Methods will now have their arguments and return types checked if they have them declared. The signatures should match the following list:

    • __call(string $name, array $arguments): mixed
    • __callStatic(string $name, array $arguments): mixed
    • __clone(): void
    • __debugInfo(): ?array
    • __get(string $name): mixed
    • __invoke(mixed $arguments): mixed
    • __isset(string $name): bool
    • __serialize(): array
    • __set(string $name, mixed $value): void
    • __set_state(array $properties): object
    • __sleep(): array
    • __unserialize(array $data): void
    • __unset(string $name): void
    • __wakeup(): void
  • call_user_func_array() array keys will now be interpreted as parameter names, instead of being silently ignored.

  • Declaring a function called assert() inside a namespace is no longer allowed, and issues E_COMPILE_ERROR. The assert() function is subject to special handling by the engine, which may lead to inconsistent behavior when defining a namespaced function with the same name.

Resource to Object Migration

Several resources have been migrated to objects. Return value checks using is_resource() should be replaced with checks for false.

COM and .Net (Windows)

The ability to import case-insensitive constants from type libraries has been removed. The second argument to com_load_typelib() may no longer be false; com.autoregister_casesensitive may no longer be disabled; case-insensitive markers in com.typelib_file are ignored.


CURLOPT_POSTFIELDS no longer accepts objects as arrays. To interpret an object as an array, perform an explicit (array) cast. The same applies to other options accepting arrays as well.

Date and Time

mktime() and gmmktime() now require at least one argument. time() can be used to get the current timestamp.


Unimplemented classes from the DOM extension that had no behavior and contained test data have been removed. These classes have also been removed in the latest version of the DOM standard:

  • DOMNameList
  • DomImplementationList
  • DOMConfiguration
  • DomError
  • DomErrorHandler
  • DOMImplementationSource
  • DOMLocator
  • DOMUserDataHandler
  • DOMTypeInfo
  • DOMStringExtend

Unimplemented methods from the DOM extension that had no behavior have been removed:

  • DOMNamedNodeMap::setNamedItem()
  • DOMNamedNodeMap::removeNamedItem()
  • DOMNamedNodeMap::setNamedItemNS()
  • DOMNamedNodeMap::removeNamedItemNS()
  • DOMText::replaceWholeText()
  • DOMNode::compareDocumentPosition()
  • DOMNode::isEqualNode()
  • DOMNode::getFeature()
  • DOMNode::setUserData()
  • DOMNode::getUserData()
  • DOMDocument::renameNode()



read_exif_data() has been removed; exif_read_data() should be used instead.


  • The FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED flags for the FILTER_VALIDATE_URL filter have been removed. The scheme and host are (and have been) always required.

  • The INPUT_REQUEST and INPUT_SESSION source for filter_input() etc. have been removed. These were never implemented and their use always generated a warning.


  • The deprecated function image2wbmp() has been removed.

  • The deprecated functions png2wbmp() and jpeg2wbmp() have been removed.

  • The default mode parameter of imagecropauto() no longer accepts -1. IMG_CROP_DEFAULT should be used instead.

  • On Windows, php_gd2.dll has been renamed to php_gd.dll.


gmp_random() has been removed. One of gmp_random_range() or gmp_random_bits() should be used instead.


iconv implementations which do not properly set errno in case of errors are no longer supported.


Internationalization Functions

  • The deprecated constant INTL_IDNA_VARIANT_2003 has been removed.

  • The deprecated Normalizer::NONE constant has been removed.




  • The OCI-Lob class is now called OCILob, and the OCI-Collection class is now called OCICollection for name compliance enforced by PHP 8 arginfo type annotation tooling.

  • Several alias functions have been marked as deprecated.

  • oci_internal_debug() and its alias ociinternaldebug() have been removed.



Regular Expressions (Perl-Compatible)

When passing invalid escape sequences they are no longer interpreted as literals. This behavior previously required the X modifier – which is now ignored.

PHP Data Objects

  • The default error handling mode has been changed from "silent" to "exceptions". See Errors and error handling for details.

  • The signatures of some PDO methods have changed:

    • PDO::query(string $query, ?int $fetchMode = null, mixed ...$fetchModeArgs)
    • PDOStatement::setFetchMode(int $mode, mixed ...$args)


The php.ini directive pdo_odbc.db2_instance_name has been removed.


PDO::inTransaction() now reports the actual transaction state of the connection, rather than an approximation maintained by PDO. If a query that is subject to "implicit commit" is executed, PDO::inTransaction() will subsequently return false, as a transaction is no longer active.


  • The deprecated pg_connect() syntax using multiple parameters instead of a connection string is no longer supported.

  • The deprecated pg_lo_import() and pg_lo_export() signature that passes the connection as the last argument is no longer supported. The connection should be passed as first argument instead.

  • pg_fetch_all() will now return an empty array instead of false for result sets with zero rows.


Metadata associated with a phar will no longer be automatically unserialized, to fix potential security vulnerabilities due to object instantiation, autoloading, etc.


  • The method signatures

    • ReflectionClass::newInstance($args)
    • ReflectionFunction::invoke($args)
    • ReflectionMethod::invoke($object, $args)

    have been changed to:

    • ReflectionClass::newInstance(...$args)
    • ReflectionFunction::invoke(...$args)
    • ReflectionMethod::invoke($object, ...$args)

    Code that must be compatible with both PHP 7 and PHP 8 can use the following signatures to be compatible with both versions:

    • ReflectionClass::newInstance($arg = null, ...$args)
    • ReflectionFunction::invoke($arg = null, ...$args)
    • ReflectionMethod::invoke($object, $arg = null, ...$args)
  • The ReflectionType::__toString() method will now return a complete debug representation of the type, and is no longer deprecated. In particular the result will include a nullability indicator for nullable types. The format of the return value is not stable and may change between PHP versions.

  • Reflection export() methods have been removed. Instead reflection objects can be cast to string.

  • ReflectionMethod::isConstructor() and ReflectionMethod::isDestructor() now also return true for __construct() and __destruct() methods of interfaces. Previously, this would only be true for methods of classes and traits.

  • ReflectionType::isBuiltin() method has been moved to ReflectionNamedType. ReflectionUnionType does not have it.


  • The deprecated AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES flags for socket_addrinfo_lookup() have been removed.

Standard PHP Library (SPL)

Standard Library

  • assert() will no longer evaluate string arguments, instead they will be treated like any other argument. assert($a == $b) should be used instead of assert('$a == $b'). The assert.quiet_eval ini directive and the ASSERT_QUIET_EVAL constant have also been removed, as they would no longer have any effect.

  • parse_str() can no longer be used without specifying a result array.

  • The string.strip_tags filter has been removed.

  • The needle argument of strpos(), strrpos(), stripos(), strripos(), strstr(), strchr(), strrchr(), and stristr() will now always be interpreted as a string. Previously non-string needles were interpreted as an ASCII code point. An explicit call to chr() can be used to restore the previous behavior.

  • The needle argument for strpos(), strrpos(), stripos(), strripos(), strstr(), stristr() and strrchr() can now be empty.

  • The length argument for substr(), substr_count(), substr_compare(), and iconv_substr() can now be null. null values will behave as if no length argument was provided and will therefore return the remainder of the string instead of an empty string.

  • The length argument for array_splice() can now be null. null values will behave identically to omitting the argument, thus removing everything from the offset to the end of the array.

  • The args argument of vsprintf(), vfprintf(), and vprintf() must now be an array. Previously any type was accepted.

  • The 'salt' option of password_hash() is no longer supported. If the 'salt' option is used a warning is generated, the provided salt is ignored, and a generated salt is used instead.

  • The quotemeta() function will now return an empty string if an empty string was passed. Previously false was returned.

  • The following functions have been removed:

  • FILTER_SANITIZE_MAGIC_QUOTES has been removed.

  • Calling implode() with parameters in a reverse order ($pieces, $glue) is no longer supported.

  • parse_url() will now distinguish absent and empty queries and fragments:

    • → query = null, fragment = null
    • → query = "", fragment = null
    • → query = null, fragment = ""
    • → query = "", fragment = ""
    Previously all cases resulted in query and fragment being null.
  • var_dump() and debug_zval_dump() will now print floating-point numbers using serialize_precision rather than precision. In a default configuration, this means that floating-point numbers are now printed with full accuracy by these debugging functions.

  • If the array returned by __sleep() contains non-existing properties, these are now silently ignored. Previously, such properties would have been serialized as if they had the value null.

  • The default locale on startup is now always "C". No locales are inherited from the environment by default. Previously, LC_ALL was set to "C", while LC_CTYPE was inherited from the environment. However, some functions did not respect the inherited locale without an explicit setlocale() call. An explicit setlocale() call is now always required if a locale component should be changed from the default.

  • The deprecated DES fallback in crypt() has been removed. If an unknown salt format is passed to crypt(), the function will fail with *0 instead of falling back to a weak DES hash now.

  • Specifying out of range rounds for SHA256/SHA512 crypt() will now fail with *0 instead of clamping to the closest limit. This matches glibc behavior.

  • The result of sorting functions may have changed, if the array contains elements that compare as equal.

  • Any functions accepting callbacks that are not explicitly specified to accept parameters by reference will now warn if a callback with reference parameters is used. Examples include array_filter() and array_reduce(). This was already the case for most, but not all, functions previously.

  • The HTTP stream wrapper as used by functions like file_get_contents() now advertises HTTP/1.1 rather than HTTP/1.0 by default. This does not change the behavior of the client, but may cause servers to respond differently. To retain the old behavior, set the 'protocol_version' stream context option, e.g.

    $ctx = stream_context_create(['http' => ['protocol_version' => '1.0']]);
    file_get_contents('', false, $ctx);
  • Calling crypt() without an explicit salt is no longer supported. If you would like to produce a strong hash with an auto-generated salt, use password_hash() instead.

  • substr(), mb_substr(), iconv_substr() and grapheme_substr() now consistently clamp out-of-bounds offsets to the string boundary. Previously, false was returned instead of the empty string in some cases.

  • On Windows, the program execution functions (proc_open(), exec(), popen() etc.) using the shell, now consistently execute %comspec% /s /c "$commandline", which has the same effect as executing $commandline (without additional quotes).


  • The auto_release parameter of sem_get() was changed to accept bool values rather than int.



  • T_COMMENT tokens will no longer include a trailing newline. The newline will instead be part of a following T_WHITESPACE token. It should be noted that T_COMMENT is not always followed by whitespace, it may also be followed by T_CLOSE_TAG or end-of-file.

  • Namespaced names are now represented using the T_NAME_QUALIFIED (Foo\Bar), T_NAME_FULLY_QUALIFIED (\Foo\Bar) and T_NAME_RELATIVE (namespace\Foo\Bar) tokens. T_NS_SEPARATOR is only used for standalone namespace separators, and only syntactially valid in conjunction with group use declarations.


XMLReader::open() and XMLReader::xml() are now static methods. They can still be called as instance methods, but inheriting classes need to declare them as static if they override these methods.


The XML-RPC extension has been moved to PECL and is no longer part of the PHP distribution.


ZipArchive::OPSYS_Z_CPM has been removed (this name was a typo). Use ZipArchive::OPSYS_CPM instead.


Windows PHP Test Packs

The test runner has been renamed from run-test.php to run-tests.php, to match its name in php-src.

PHP 7.4

Backward Incompatible Changes

PHP Core

Array-style access of non-arrays

Trying to use values of type null, bool, int, float or resource as an array (such as $null["key"]) will now generate a notice.

get_declared_classes() function

The get_declared_classes() function no longer returns anonymous classes that have not been instantiated yet.

fn keyword

fn is now a reserved keyword. In particular, it can no longer be used as a function or class name. It can still be used as a method or class constant name.

tag at end of file

at the end of the file (without trailing newline) will now be interpreted as an opening PHP tag. Previously it was interpreted either as a short opening tag followed by literal php and resulted in a syntax error (with short_open_tag=1) or was interpreted as a literal string (with short_open_tag=0).

Stream wrappers

When using include/require on a stream, streamWrapper::stream_set_option() will be invoked with the STREAM_OPTION_READ_BUFFER option. Custom stream wrapper implementations may need to implement the streamWrapper::stream_set_option() method to avoid a warning (always returning false is a sufficient implementation).


The o serialization format has been removed. As it is never produced by PHP, this may only break unserialization of manually crafted strings.

Password algorithm constants

Password hashing algorithm identifiers are now nullable strings rather than integers.

  • PASSWORD_DEFAULT was int 1; now is string '2y' (in PHP 7.4.0, 7.4.1, and 7.4.2 it was null)
  • PASSWORD_BCRYPT was int 1; now is string '2y'
  • PASSWORD_ARGON2I was int 2; now is string 'argon2i'
  • PASSWORD_ARGON2ID was int 3; now is string 'argon2id'

Applications correctly using the constants PASSWORD_DEFAULT, PASSWORD_BCRYPT, PASSWORD_ARGON2I, and PASSWORD_ARGON2ID will continue to function correctly.

htmlentities() function

htmlentities() will now raise a notice (instead of a strict standards warning) if it is used with an encoding for which only basic entity substitution is supported, in which case it is equivalent to htmlspecialchars().

fread() and fwrite() function

fread() and fwrite() will now return false if the operation failed. Previously an empty string or 0 was returned. EAGAIN/EWOULDBLOCK are not considered failures.

These functions now also raise a notice on failure, such as when trying to write to a read-only file resource.

BCMath Arbitrary Precision Mathematics

BCMath functions will now warn if a non well-formed number is passed, such as "32foo". The argument will be interpreted as zero, as before.


Attempting to serialize a CURLFile class will now generate an exception. Previously the exception was only thrown on unserialization.

Using CURLPIPE_HTTP1 is deprecated, and is no longer supported as of cURL 7.62.0.

The $version parameter of curl_version() is deprecated. If any value not equal to the default CURLVERSION_NOW is passed, a warning is raised and the parameter is ignored.

Date and Time

Calling var_dump() or similar on a DateTime or DateTimeImmutable instance will no longer leave behind accessible properties on the object.

Comparison of DateInterval objects (using ==, <, and so on) will now generate a warning and always return false. Previously all DateInterval objects were considered equal, unless they had properties.


The default parameter value of idn_to_ascii() and idn_to_utf8() is now INTL_IDNA_VARIANT_UTS46 instead of the deprecated INTL_IDNA_VARIANT_2003.


The embedded server functionality has been removed. It was broken since at least PHP 7.0.

The undocumented mysqli::$stat property has been removed in favor of mysqli::stat().


The openssl_random_pseudo_bytes() function will now throw an exception in error situations, similar to random_bytes(). In particular, an Error is thrown if the number of requested bytes is less than or equal to zero, and an Exception is thrown if sufficient randomness cannot be gathered. The $crypto_strong output argument is guaranteed to always be true if the function does not throw, so explicitly checking it is not necessary.

Regular Expressions (Perl-Compatible)

When PREG_UNMATCHED_AS_NULL mode is used, trailing unmatched capturing groups will now also be set to null (or [null, -1] if offset capture is enabled). This means that the size of the $matches will always be the same.

PHP Data Objects

Attempting to serialize a PDO or PDOStatement instance will now generate an Exception rather than a PDOException, consistent with other internal classes which do not support serialization.


Reflection objects will now generate an exception if an attempt is made to serialize them. Serialization for reflection objects was never supported and resulted in corrupted reflection objects. It has been explicitly prohibited now.

The values of the class constant of ReflectionClassConstant, ReflectionMethod and ReflectionProperty have changed.

Standard PHP Library (SPL)

Calling get_object_vars() on an ArrayObject instance will now always return the properties of the ArrayObject itself (or a subclass). Previously it returned the values of the wrapped array/object unless the ArrayObject::STD_PROP_LIST flag was specified.

Other affected operations are:

(array) casts are not affected. They will continue to return either the wrapped array, or the ArrayObject properties, depending on whether the ArrayObject::STD_PROP_LIST flag is used.

SplPriorityQueue::setExtractFlags() will throw an exception if zero is passed. Previously this would generate a recoverable fatal error on the next extraction operation.

ArrayObject, ArrayIterator, SplDoublyLinkedList and SplObjectStorage now support the __serialize() and __unserialize() mechanism in addition to the Serializable interface. This means that serialization payloads created on older PHP versions can still be unserialized, but new payloads created by PHP 7.4 will not be understood by older versions.


token_get_all() will now emit a T_BAD_CHARACTER token for unexpected characters instead of leaving behind holes in the token stream.

Incoming Cookies

As of PHP 7.4.11, the names of incoming cookies are no longer url-decoded for security reasons.

PHP 7.3

Backward Incompatible Changes

PHP Core

Heredoc/Nowdoc Ending Label Interpretation

Due to the introduction of flexible heredoc/nowdoc syntax, doc strings that contain the ending label inside their body may cause syntax errors or change in interpretation. For example in:

$str = <<abcdefg
the indented occurrence of FOO did not previously have any special meaning. Now it will be interpreted as the end of the heredoc string and the following FOO; will cause a syntax error. This issue can always be resolved by choosing an ending label that does not occur within the contents of the string.

Continue Targeting Switch issues Warning

continue statements targeting switch control flow structures will now generate a warning. In PHP such continue statements are equivalent to break, while they behave as continue 2 in other languages.

while ($foo) {
switch (
$bar) {
// Warning: "continue" targeting switch is equivalent to
// "break". Did you mean to use "continue 2"?

Strict Interpretation of Integer String Keys on ArrayAccess

Array accesses of type $obj["123"], where $obj implements ArrayAccess and "123" is an integer string literal will no longer result in an implicit conversion to integer, i.e., $obj->offsetGet("123") will be called instead of $obj->offsetGet(123). This matches existing behavior for non-literals. The behavior of arrays is not affected in any way, they continue to implicitly convert integral string keys to integers.

Static Properties no longer separated by Reference Assignment

In PHP, static properties are shared between inheriting classes, unless the static property is explicitly overridden in a child class. However, due to an implementation artifact it was possible to separate the static properties by assigning a reference. This loophole has been fixed.

class Test {
public static
$x = 0;
Test2 extends Test { }

Test2::$x = &$x;
$x = 1;

var_dump(Test::$x, Test2::$x);
// Previously: int(0), int(1)
// Now: int(1), int(1)

References returned by Array and Property Accesses are immediately unwrapped

References returned by array and property accesses are now unwrapped as part of the access. This means that it is no longer possible to modify the reference between the access and the use of the accessed value:

$arr = [1];
$ref =& $arr[0];
var_dump($arr[0] + ($arr[0] = 2));
// Previously: int(4), Now: int(3)
This makes the behavior of references and non-references consistent. Please note that reading and writing a value inside a single expression remains undefined behavior and may change again in the future.

Argument Unpacking of Traversables with non-Integer Keys no longer supported

Argument unpacking stopped working with Traversables with non-integer keys. The following code worked in PHP 5.6-7.2 by accident.

function foo(...$args) {
gen() {
1.23 => 123;


The ext_skel utility has been completely redesigned with new options and some old options removed. This is now written in PHP and has no external dependencies.

Support for BeOS has been dropped.

Exceptions thrown due to automatic conversion of warnings into exceptions in EH_THROW mode (e.g. some DateTime exceptions) no longer populate error_get_last() state. As such, they now work the same way as manually thrown exceptions.

TypeError now reports wrong types as int and bool instead of integer and boolean, respectively.

Undefined variables passed to compact() will now be reported as a notice.

getimagesize() and related functions now report the mime type of BMP images as image/bmp instead of image/x-ms-bmp, since the former has been registered with the IANA (see » RFC 7903).

stream_socket_get_name() will now return IPv6 addresses wrapped in brackets. For example "[::1]:1337" will be returned instead of "::1:1337".

BCMath Arbitrary Precision Mathematics

All warnings thrown by BCMath functions are now using PHP's error handling. Formerly some warnings have directly been written to stderr.

bcmul() and bcpow() now return numbers with the requested scale. Formerly, the returned numbers may have omitted trailing decimal zeroes.


rsh/ssh logins are disabled by default. Use imap.enable_insecure_rsh if you want to enable them. Note that the IMAP library does not filter mailbox names before passing them to the rsh/ssh command, thus passing untrusted data to this function with rsh/ssh enabled is insecure.

Multibyte String

Due to added support for named captures, mb_ereg_*() patterns using named captures will behave differently. In particular named captures will be part of matches and mb_ereg_replace() will interpret additional syntax. See Named Captures for more information.

MySQL Improved Extension

Prepared statements now properly report the fractional seconds for DATETIME, TIME and TIMESTAMP columns with decimals specifier (e.g. TIMESTAMP(6) when using microseconds). Formerly, the fractional seconds part was simply omitted from the returned values.

MySQL Functions (PDO_MYSQL)

Prepared statements now properly report the fractional seconds for DATETIME, TIME and TIMESTAMP columns with decimals specifier (e.g. TIMESTAMP(6) when using microseconds). Formerly, the fractional seconds part was simply omitted from the returned values. Please note that this only affects the usage of PDO_MYSQL with emulated prepares turned off (e.g. using the native preparation functionality). Statements using connections having PDO::ATTR_EMULATE_PREPARES=true (which is the default) were not affected by the bug fixed and have already been getting the proper fractional seconds values from the engine.


Reflection export to string now uses int and bool instead of integer and boolean, respectively.

Standard PHP Library (SPL)

If an SPL autoloader throws an exception, following autoloaders will not be executed. Previously all autoloaders were executed and exceptions were chained.


Mathematic operations involving SimpleXML objects will now treat the text as an int or float, whichever is more appropriate. Previously values were treated as ints unconditionally.

Incoming Cookies

As of PHP 7.3.23, the names of incoming cookies are no longer url-decoded for security reasons.

PHP 7.2

Backward incompatible changes

Prevent number_format() from returning negative zero

Previously, it was possible for the number_format() function to return -0. Whilst this is perfectly valid according to the IEEE 754 floating point specification, this oddity was not desirable for displaying formatted numbers in a human-readable form.

(number_format(-0.01)); // now outputs string(1) "0" instead of string(2) "-0"

Convert numeric keys in object and array casts

Numeric keys are now better handled when casting arrays to objects and objects to arrays (either from explicit casting or by settype()).

This means that integer (or stringy integer) keys from arrays being casted to objects are now accessible:

// array to object
$arr = [0 => 1];
$obj = (object)$arr;
$obj->{'0'}, // now accessible
$obj->{0} // now accessible

The above example will output:

object(stdClass)#1 (1) {
  ["0"]=>    // string key now, rather than integer key

And integer (or stringy integer) keys from objects being casted to arrays are now accessible:

// object to array
$obj = new class {
public function
$this->{0} = 1;
$arr = (array)$obj;
$arr[0], // now accessible
$arr['0'] // now accessible

The above example will output:

array(1) {
  [0]=>    // integer key now, rather than string key

Disallow passing null to get_class()

Previously, passing null to the get_class() function would output the name of the enclosing class. This behaviour has now been removed, where an E_WARNING will be output instead. To achieve the same behaviour as before, the argument should simply be omitted.

Warn when counting non-countable types

An E_WARNING will now be emitted when attempting to count() non-countable types (this includes the sizeof() alias function).

count(null), // NULL is not countable
count(1), // integers are not countable
count('abc'), // strings are not countable
count(new stdClass), // objects not implementing the Countable interface are not countable
count([1,2]) // arrays are countable

The above example will output:

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d

Move ext/hash from resources to objects

As part of the long-term migration away from resources, the Hash extension has been updated to use objects instead of resources. The change should be seamless for PHP developers, except for where is_resource() checks have been made (which will need updating to is_object() instead).

Improve SSL/TLS defaults

The following changes to the defaults have been made:

  • tls:// now defaults to TLSv1.0 or TLSv1.1 or TLSv1.2
  • ssl:// an alias of tls://
  • STREAM_CRYPTO_METHOD_TLS_* constants default to TLSv1.0 or TLSv1.1 + TLSv1.2, instead of TLSv1.0 only

gettype() return value on closed resources

Previously, using gettype() on a closed resource would return a string of "unknown type". Now, a string of "resource (closed)" will be returned.

is_object() and __PHP_Incomplete_Class

Previously, using is_object() on the __PHP_Incomplete_Class class would return false. Now, true will be returned.

Promote the error level of undefined constants

Unqualified references to undefined constants will now generate an E_WARNING (instead of an E_NOTICE). In the next major version of PHP, they will generate Error exceptions.

Windows support

The officially supported, minimum Windows versions are now Windows 7/Server 2008 R2.

Checks on default property values of traits

Compatibility checks upon default trait property values will no longer perform casting.

object for class names

The object name was previously soft-reserved in PHP 7.0. This is now hard-reserved, prohibiting it from being used as a class, trait, or interface name.

NetWare support

Support for NetWare has now been removed.

array_unique() with SORT_STRING

While array_unique() with SORT_STRING formerly copied the array and removed non-unique elements (without packing the array afterwards), now a new array is built by adding the unique elements. This can result in different numeric indexes.

bcmod() changes with floats

The bcmod() function no longer truncates fractional numbers to integers. As such, its behavior now follows fmod(), rather than the % operator. For example bcmod('4', '3.5') now returns 0.5 instead of 1.

Hashing functions and non-cryptographic hashes

The hash_hmac(), hash_hmac_file(), hash_pbkdf2(), and hash_init() (with HASH_HMAC) functions no longer accept non-cryptographic hashes.

json_decode() function options

The json_decode() function option, JSON_OBJECT_AS_ARRAY, is now used if the second parameter (assoc) is null. Previously, JSON_OBJECT_AS_ARRAY was always ignored.

rand() and mt_rand() output

Sequences generated by rand() and mt_rand() for a specific seed may differ from PHP 7.1 on 64-bit machines (due to the fixing of a modulo bias bug in the implementation).

Removal of sql.safe_mode ini setting

The sql.safe_mode ini setting has now been removed.

Changes to date_parse() and date_parse_from_format()

The zone element of the array returned by date_parse() and date_parse_from_format() represents seconds instead of minutes now, and its sign is inverted. For instance -120 is now 7200.

Incoming Cookies

As of PHP 7.2.34, the names of incoming cookies are no longer url-decoded for security reasons.

PHP 7.1

Backward incompatible changes

Throw on passing too few function arguments

Previously, a warning would be emitted for invoking user-defined functions with too few arguments. Now, this warning has been promoted to an Error exception. This change only applies to user-defined functions, not internal functions. For example:

function test($param){}

The above example will output something similar to:

Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passed in %s on line %d and exactly 1 expected in %s:%d

Forbid dynamic calls to scope introspection functions

Dynamic calls for certain functions have been forbidden (in the form of $func() or array_map('extract', ...), etc). These functions either inspect or modify another scope, and present with them ambiguous and unreliable behavior. The functions are as follows:

(function () {
$func = 'func_num_args';

The above example will output:

Warning: Cannot call func_num_args() dynamically in %s on line %d

Invalid class, interface, and trait names

The following names cannot be used to name classes, interfaces, or traits:

Numerical string conversions now respect scientific notation

Integer operations and conversions on numerical strings now respect scientific notation. This also includes the (int) cast operation, and the following functions: intval() (where the base is 10), settype(), decbin(), decoct(), and dechex().

Fixes to mt_rand() algorithm

mt_rand() will now default to using the fixed version of the Mersenne Twister algorithm. If deterministic output from mt_rand() was relied upon, then MT_RAND_PHP can be used as optional second parameter to mt_srand() to preserve the old (incorrect) implementation.

rand() aliased to mt_rand() and srand() aliased to mt_srand()

rand() and srand() have now been made aliases to mt_rand() and mt_srand(), respectively. This means that the output for the following functions have changed: rand(), shuffle(), str_shuffle(), and array_rand().

Disallow the ASCII delete control character in identifiers

The ASCII delete control character (0x7F) can no longer be used in identifiers that are not quoted.

error_log changes with syslog value

If the error_log ini setting is set to syslog, the PHP error levels are mapped to the syslog error levels. This brings finer differentiation in the error logs in contrary to the previous approach where all the errors are logged with the notice level only.

Do not call destructors on incomplete objects

Destructors are now never called for objects that throw an exception during the execution of their constructor. In previous versions this behavior depended on whether the object was referenced outside the constructor (e.g. by an exception backtrace).

call_user_func() handling of reference arguments

call_user_func() will now always generate a warning upon calls to functions that expect references as arguments. Previously this depended on whether the call was fully qualified.

Additionally, call_user_func() and call_user_func_array() will no longer abort the function call in this case. The "expected reference" warning will be emitted, but the call will proceed as usual.

The empty index operator is not supported for strings anymore

Applying the empty index operator to a string (e.g. $str[] = $x) throws a fatal error instead of converting silently to array.

Assignment via string index access on an empty string

String modification by character on an empty string now works like for non-empty strings, i.e. writing to an out of range offset pads the string with spaces, where non-integer types are converted to integer, and only the first character of the assigned string is used. Formerly, empty strings where silently treated like an empty array.

$a = '';
$a[10] = 'foo';

Output of the above example in PHP 7.0:

array(1) {
  string(3) "foo"

Output of the above example in PHP 7.1:

string(11) "          f"

Removed ini directives

The following ini directives have been removed:

  • session.entropy_file
  • session.entropy_length
  • session.hash_function
  • session.hash_bits_per_character

Array ordering when elements are automatically created during by reference assignments has changed

The order of the elements in an array has changed when those elements have been automatically created by referencing them in a by reference assignment. For example:

$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;

Output of the above example in PHP 7.0:

array(2) {

Output of the above example in PHP 7.1:

array(2) {

Sort order of equal elements

The internal sorting algorithm has been improved, what may result in different sort order of elements, which compare as equal, than before.


Don't rely on the order of elements which compare as equal; it might change anytime.

Error message for E_RECOVERABLE errors

The error message for E_RECOVERABLE errors has been changed from "Catchable fatal error" to "Recoverable fatal error".

$options parameter of unserialize()

The allowed_classes element of the $options parameter of unserialize() is now strictly typed, i.e. if anything other than an array or a bool is given, unserialize() returns false and issues an E_WARNING.

DateTime constructor incorporates microseconds

DateTime and DateTimeImmutable now properly incorporate microseconds when constructed from the current time, either explicitly or with a relative string (e.g. "first day of next month"). This means that naive comparisons of two newly created instances will now more likely return false instead of true:

new DateTime() == new DateTime();

Fatal errors to Error exceptions conversions

In the Date extension, invalid serialization data for DateTime or DatePeriod classes, or timezone initialization failure from serialized data, will now throw an Error exception from the __wakeup() or __set_state() methods, instead of resulting in a fatal error.

In the DBA extension, data modification functions (such as dba_insert()) will now throw an Error exception instead of triggering a catchable fatal error if the key does not contain exactly two elements.

In the DOM extension, invalid schema or RelaxNG validation contexts will now throw an Error exception instead of resulting in a fatal error. Similarly, attempting to register a node class that does not extend the appropriate base class, or attempting to read an invalid property or write to a readonly property, will also now throw an Error exception.

In the IMAP extension, email addresses longer than 16385 bytes will throw an Error exception instead of resulting in a fatal error.

In the Intl extension, failing to call the parent constructor in a class extending Collator before invoking the parent methods will now throw an Error instead of resulting in a recoverable fatal error. Also, cloning a Transliterator object will now throw an Error exception on failure to clone the internal transliterator instead of resulting in a fatal error.

In the LDAP extension, providing an unknown modification type to ldap_batch_modify() will now throw an Error exception instead of resulting in a fatal error.

In the mbstring extension, the mb_ereg() and mb_eregi() functions will now throw a ParseError exception if an invalid PHP expression is provided and the 'e' option is used.

In the Mcrypt extension, the mcrypt_encrypt() and mcrypt_decrypt() will now throw an Error exception instead of resulting in a fatal error if mcrypt cannot be initialized.

In the mysqli extension, attempting to read an invalid property or write to a readonly property will now throw an Error exception instead of resulting in a fatal error.

In the Reflection extension, failing to retrieve a reflection object or retrieve an object property will now throw an Error exception instead of resulting in a fatal error.

In the Session extension, custom session handlers that do not return strings for session IDs will now throw an Error exception instead of resulting in a fatal error when a function is called that must generate a session ID.

In the SimpleXML extension, creating an unnamed or duplicate attribute will now throw an Error exception instead of resulting in a fatal error.

In the SPL extension, attempting to clone an SplDirectory object will now throw an Error exception instead of resulting in a fatal error. Similarly, calling ArrayIterator::append() when iterating over an object will also now throw an Error exception.

In the standard extension, the assert() function, when provided with a string argument as its first parameter, will now throw a ParseError exception instead of resulting in a catchable fatal error if the PHP code is invalid. Similarly, calling forward_static_call() outside of a class scope will now throw an Error exception.

In the Tidy extension, creating a tidyNode manually will now throw an Error exception instead of resulting in a fatal error.

In the WDDX extension, a circular reference when serializing will now throw an Error exception instead of resulting in a fatal error.

In the XML-RPC extension, a circular reference when serializing will now throw an instance of Error exception instead of resulting in a fatal error.

In the Zip extension, the ZipArchive::addGlob() method will now throw an Error exception instead of resulting in a fatal error if glob support is not available.

Lexically bound variables cannot reuse names

Variables bound to a closure via the use construct cannot use the same name as any superglobals, $this, or any parameter. For example, all of these function definition will result in a fatal error:

$f = function () use ($_SERVER) {};
$f = function () use ($this) {};
$f = function ($param) use ($param) {};

long2ip() parameter type change

long2ip() now expects an int instead of a string.

JSON encoding and decoding

The serialize_precision ini setting now controls the serialization precision when encoding floats.

Decoding an empty key now results in an empty property name, rather than _empty_ as a property name.

var_dump(json_decode(json_encode(['' => 1])));

The above example will output something similar to:

object(stdClass)#1 (1) {

When supplying the JSON_UNESCAPED_UNICODE flag to json_encode(), the sequences U+2028 and U+2029 are now escaped.

Changes to mb_ereg() and mb_eregi() parameter semantics

The third parameter to the mb_ereg() and mb_eregi() functions (regs) will now be set to an empty array if nothing was matched. Formerly, the parameter would not have been modified.

Drop support for the sslv2 stream

The sslv2 stream has now been dropped in OpenSSL.

Forbid "return;" for typed returns already at compile-time

Return statements without argument in functions which declare a return type now trigger E_COMPILE_ERROR (unless the return type is declared as void), even if the return statement would never be reached.

PHP 7.0

Backward incompatible changes

Changes to error and exception handling

Many fatal and recoverable fatal errors have been converted to exceptions in PHP 7. These error exceptions inherit from the Error class, which itself implements the Throwable interface (the new base interface all exceptions inherit).

This means that custom error handlers may no longer be triggered because exceptions may be thrown instead (causing new fatal errors for uncaught Error exceptions).

A fuller description of how errors operate in PHP 7 can be found on the PHP 7 errors page. This migration guide will merely enumerate the changes that affect backward compatibility.

set_exception_handler() is no longer guaranteed to receive Exception objects

Code that implements an exception handler registered with set_exception_handler() using a type declaration of Exception will cause a fatal error when an Error object is thrown.

If the handler needs to work on both PHP 5 and 7, you should remove the type declaration from the handler, while code that is being migrated to work on PHP 7 exclusively can simply replace the Exception type declaration with Throwable instead.

// PHP 5 era code that will break.
function handler(Exception $e) { ... }

// PHP 5 and 7 compatible.
function handler($e) { ... }

// PHP 7 only.
function handler(Throwable $e) { ... }

Internal constructors always throw exceptions on failure

Previously, some internal classes would return null or an unusable object when the constructor failed. All internal classes will now throw an Exception in this case in the same way that user classes already had to.

Parse errors throw ParseError

Parser errors now throw a ParseError object. Error handling for eval() should now include a catch block that can handle this error.

E_STRICT notices severity changes

All of the E_STRICT notices have been reclassified to other levels. E_STRICT constant is retained, so calls like error_reporting(E_ALL|E_STRICT) will not cause an error.

E_STRICT notices severity changes
Situation New level/behaviour
Indexing by a resource E_NOTICE
Abstract static methods Notice removed, triggers no error
"Redefining" a constructor Notice removed, triggers no error
Signature mismatch during inheritance E_WARNING
Same (compatible) property in two used traits Notice removed, triggers no error
Accessing static property non-statically E_NOTICE
Only variables should be assigned by reference E_NOTICE
Only variables should be passed by reference E_NOTICE
Calling non-static methods statically E_DEPRECATED

Changes to variable handling

PHP 7 now uses an abstract syntax tree when parsing source files. This has permitted many improvements to the language which were previously impossible due to limitations in the parser used in earlier versions of PHP, but has resulted in the removal of a few special cases for consistency reasons, which has resulted in backward compatibility breaks. These cases are detailed in this section.

Changes to the handling of indirect variables, properties, and methods

Indirect access to variables, properties, and methods will now be evaluated strictly in left-to-right order, as opposed to the previous mix of special cases. The table below shows how the order of evaluation has changed.

Old and new evaluation of indirect expressions
Expression PHP 5 interpretation PHP 7 interpretation
$$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz']
$foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz']
$foo->$bar['baz']() $foo->{$bar['baz']}() ($foo->$bar)['baz']()
Foo::$bar['baz']() Foo::{$bar['baz']}() (Foo::$bar)['baz']()

Code that used the old right-to-left evaluation order must be rewritten to explicitly use that evaluation order with curly braces (see the above middle column). This will make the code both forwards compatible with PHP 7.x and backwards compatible with PHP 5.x.

This also affects the global keyword. The curly brace syntax can be used to emulate the previous behaviour if required:

function f() {
// Valid in PHP 5 only.
global $$foo->bar;

// Valid in PHP 5 and 7.
global ${$foo->bar};

Changes to list() handling

list() no longer assigns variables in reverse order

list() will now assign values to variables in the order they are defined, rather than reverse order. In general, this only affects the case where list() is being used in conjunction with the array [] operator, as shown below:

list($a[], $a[], $a[]) = [1, 2, 3];

Output of the above example in PHP 5:

array(3) {

Output of the above example in PHP 7:

array(3) {

In general, it is recommended not to rely on the order in which list() assignments occur, as this is an implementation detail that may change again in the future.

Empty list() assignments have been removed

list() constructs can no longer be empty. The following are no longer allowed:

list() = $a;
list(,,) =
$x, list(), $y) = $a;
list() cannot unpack strings

list() can no longer unpack string variables. str_split() should be used instead.

Array ordering when elements are automatically created during by reference assignments has changed

The order of the elements in an array has changed when those elements have been automatically created by referencing them in a by reference assignment. For example:

$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;

Output of the above example in PHP 5:

array(2) {

Output of the above example in PHP 7:

array(2) {

Parentheses around function arguments no longer affect behaviour

In PHP 5, using redundant parentheses around a function argument could quiet strict standards warnings when the function argument was passed by reference. The warning will now always be issued.

function getArray() {
return [
1, 2, 3];

squareArray(array &$a) {
foreach (
$a as &$v) {
$v **= 2;

// Generates a warning in PHP 7.

The above example will output:

Notice: Only variables should be passed by reference in /tmp/test.php on line 13

Changes to foreach

Minor changes have been made to the behaviour of the foreach control structure, primarily around the handling of the internal array pointer and modification of the array being iterated over.

foreach no longer changes the internal array pointer

Prior to PHP 7, the internal array pointer was modified while an array was being iterated over with foreach. This is no longer the case, as shown in the following example:

$array = [0, 1, 2];
foreach (
$array as &$val) {

Output of the above example in PHP 5:


Output of the above example in PHP 7:


foreach by-value operates on a copy of the array

When used in the default by-value mode, foreach will now operate on a copy of the array being iterated rather than the array itself. This means that changes to the array made during iteration will not affect the values that are iterated.

foreach by-reference has improved iteration behaviour

When iterating by-reference, foreach will now do a better job of tracking changes to the array made during iteration. For example, appending to an array while iterating will now result in the appended values being iterated over as well:

$array = [0];
foreach (
$array as &$val) {
$array[1] = 1;

Output of the above example in PHP 5:


Output of the above example in PHP 7:


Iteration of non-Traversable objects

Iterating over a non-Traversable object will now have the same behaviour as iterating over by-reference arrays. This results in the improved behaviour when modifying an array during iteration also being applied when properties are added to or removed from the object.

Changes to int handling

Invalid octal literals

Previously, octal literals that contained invalid numbers were silently truncated (0128 was taken as 012). Now, an invalid octal literal will cause a parse error.

Negative bitshifts

Bitwise shifts by negative numbers will now throw an ArithmeticError:

var_dump(1 >> -1);

Output of the above example in PHP 5:


Output of the above example in PHP 7:

Fatal error: Uncaught ArithmeticError: Bit shift by negative number in /tmp/test.php:2
Stack trace:
#0 {main}
  thrown in /tmp/test.php on line 2

Out of range bitshifts

Bitwise shifts (in either direction) beyond the bit width of an int will always result in 0. Previously, the behaviour of such shifts was architecture dependent.

Changes to Division By Zero

Previously, when 0 was used as the divisor for either the divide (/) or modulus (%) operators, an E_WARNING would be emitted and false would be returned. Now, the divide operator returns a float as either +INF, -INF, or NAN, as specified by IEEE 754. The modulus operator E_WARNING has been removed and will throw a DivisionByZeroError exception.


Output of the above example in PHP 5:

Warning: Division by zero in %s on line %d

Warning: Division by zero in %s on line %d

Warning: Division by zero in %s on line %d

Output of the above example in PHP 7:

Warning: Division by zero in %s on line %d

Warning: Division by zero in %s on line %d

PHP Fatal error:  Uncaught DivisionByZeroError: Modulo by zero in %s line %d

Changes to string handling

Hexadecimal strings are no longer considered numeric

Strings containing hexadecimal numbers are no longer considered to be numeric. For example:

var_dump("0x123" == "291");
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));

Output of the above example in PHP 5:

string(2) "oo"

Output of the above example in PHP 7:


Notice: A non well formed numeric value encountered in /tmp/test.php on line 5
string(3) "foo"

filter_var() can be used to check if a string contains a hexadecimal number, and also to convert a string of that type to an int:

$str = "0xffff";
if (
false === $int) {
throw new
Exception("Invalid integer!");
var_dump($int); // int(65535)

\u{ may cause errors

Due to the addition of the new Unicode codepoint escape syntax, strings containing a literal \u{ followed by an invalid sequence will cause a fatal error. To avoid this, the leading backslash should be escaped.

Removed functions

call_user_method() and call_user_method_array()

These functions were deprecated in PHP 4.1.0 in favour of call_user_func() and call_user_func_array(). You may also want to consider using variable functions and/or the ... operator.

All ereg* functions

All ereg functions were removed. PCRE is a recommended alternative.

mcrypt aliases

The deprecated mcrypt_generic_end() function has been removed in favour of mcrypt_generic_deinit().

Additionally, the deprecated mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb() functions have been removed in favour of using mcrypt_decrypt() with the appropriate MCRYPT_MODE_* constant.

All ext/mysql functions

All ext/mysql functions were removed. For details about choosing a different MySQL API, see Choosing a MySQL API.

All ext/mssql functions

All ext/mssql functions were removed.

intl aliases

The deprecated datefmt_set_timezone_id() and IntlDateFormatter::setTimeZoneID() aliases have been removed in favour of datefmt_set_timezone() and IntlDateFormatter::setTimeZone(), respectively.


set_magic_quotes_runtime(), along with its alias magic_quotes_runtime(), have been removed. They were deprecated in PHP 5.3.0, and became effectively non-functional with the removal of magic quotes in PHP 5.4.0.


The deprecated set_socket_blocking() alias has been removed in favour of stream_set_blocking().

dl() in PHP-FPM

dl() can no longer be used in PHP-FPM. It remains functional in the CLI and embed SAPIs.

GD Type1 functions

Support for PostScript Type1 fonts has been removed from the GD extension, resulting in the removal of the following functions:

  • imagepsbbox()
  • imagepsencodefont()
  • imagepsextendfont()
  • imagepsfreefont()
  • imagepsloadfont()
  • imagepsslantfont()
  • imagepstext()

Using TrueType fonts and their associated functions is recommended instead.

Removed INI directives

Removed features

The following INI directives have been removed as their associated features have also been removed:

  • always_populate_raw_post_data
  • asp_tags


The xsl.security_prefs directive has been removed. Instead, the XsltProcessor::setSecurityPrefs() method should be called to control the security preferences on a per-processor basis.

Other backward incompatible changes

New objects cannot be assigned by reference

The result of the new statement can no longer be assigned to a variable by reference:

class C {}
$c =& new C;

Output of the above example in PHP 5:

Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3

Output of the above example in PHP 7:

Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3

Invalid class, interface and trait names

The following names cannot be used to name classes, interfaces or traits:

  • bool
  • int
  • float
  • string
  • null
  • true
  • false

Furthermore, the following names should not be used. Although they will not generate an error in PHP 7.0, they are reserved for future use and should be considered deprecated.

  • resource
  • object
  • mixed
  • numeric

ASP and script PHP tags removed

Support for using ASP and script tags to delimit PHP code has been removed. The affected tags are:

Removed ASP and script tags
Opening tag Closing tag
<% %>
<%= %>

Calls from incompatible context removed

Previously deprecated in PHP 5.6, static calls made to a non-static method with an incompatible context will now result in the called method having an undefined $this variable and a deprecation warning being issued.

class A {
public function
test() { var_dump($this); }

// Note: Does NOT extend A
class B {
public function
callNonStaticMethodOfA() { A::test(); }


Output of the above example in PHP 5.6:

Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {

Output of the above example in PHP 7:

Deprecated: Non-static method A::test() should not be called statically in /tmp/test.php on line 8

Notice: Undefined variable: this in /tmp/test.php on line 3

yield is now a right associative operator

The yield construct no longer requires parentheses, and has been changed to a right associative operator with precedence between print and =>. This can result in changed behaviour:

echo yield -1;
// Was previously interpreted as
echo (yield) - 1;
// And is now interpreted as
echo yield (-1);

$foo or die;
// Was previously interpreted as
yield ($foo or die);
// And is now interpreted as
(yield $foo) or die;

Parentheses can be used to disambiguate those cases.

Functions cannot have multiple parameters with the same name

It is no longer possible to define two or more function parameters with the same name. For example, the following function will trigger an E_COMPILE_ERROR:

function foo($a, $b, $unused, $unused) {

Functions inspecting arguments report the current parameter value

func_get_arg(), func_get_args(), debug_backtrace() and exception backtraces will no longer report the original value that was passed to a parameter, but will instead provide the current value (which might have been modified).

function foo($x) {

Output of the above example in PHP 5:


Output of the above example in PHP 7:


Switch statements cannot have multiple default blocks

It is no longer possible to define two or more default blocks in a switch statement. For example, the following switch statement will trigger an E_COMPILE_ERROR:

switch (1) {


$HTTP_RAW_POST_DATA is no longer available. The php://input stream should be used instead.

# comments in INI files removed

Support for prefixing comments with # in INI files has been removed. ; (semi-colon) should be used instead. This change applies to php.ini, as well as files handled by parse_ini_file() and parse_ini_string().

JSON extension replaced with JSOND

The JSON extension has been replaced with JSOND, causing three minor BC breaks. Firstly, a number must not end in a decimal point (i.e. 34. must be changed to either 34.0 or 34). Secondly, when using scientific notation, the e exponent must not immediately follow a decimal point (i.e. 3.e3 must be changed to either 3.0e3 or 3e3). Finally, an empty string is no longer considered valid JSON.

Internal function failure on overflow

Previously, internal functions would silently truncate numbers produced from float-to-integer coercions when the float was too large to represent as an integer. Now, an E_WARNING will be emitted and null will be returned.

Fixes to custom session handler return values

Any predicate functions implemented by custom session handlers that return either false or -1 will be fatal errors. If any value from these functions other than a boolean, -1, or 0 is returned, then it will fail and an E_WARNING will be emitted.

Sort order of equal elements

The internal sorting algorithm has been improved, what may result in different sort order of elements, which compare as equal, than before.


Don't rely on the order of elements which compare as equal; it might change anytime.

Misplaced break and continue statements

break and continue statements outside of a loop or switch control structure are now detected at compile-time instead of run-time as before, and trigger an E_COMPILE_ERROR.

Mhash is not an extension anymore

The Mhash extension has been fully integrated into the Hash extension. Therefore, it is no longer possible to detect Mhash support with extension_loaded(); use function_exists() instead. Furthermore, Mhash is no longer reported by get_loaded_extensions() and related features.


The declare(ticks) directive does no longer leak into different compilation units.

PHP 5.6

Backward incompatible changes

Although most existing PHP 5 code should work without changes, please take note of some backward incompatible changes:

Array keys won't be overwritten when defining an array as a property of a class via an array literal

Previously, arrays declared as class properties which mixed explicit and implicit keys could have array elements silently overwritten if an explicit key was the same as a sequential implicit key. For example:

class C {
ONE = 1;
$array = [
self::ONE => 'foo',

var_dump((new C)->array);

Output of the above example in PHP 5.5:

array(2) {
  string(3) "bar"
  string(4) "quux"

Output of the above example in PHP 5.6:

array(3) {
  string(3) "foo"
  string(3) "bar"
  string(4) "quux"

json_decode() strictness

json_decode() now rejects non-lowercase variants of the JSON literals true, false and null at all times, as per the JSON specification, and sets json_last_error() accordingly. Previously, inputs to json_decode() that consisted solely of one of these values in upper or mixed case were accepted.

This change will only affect cases where invalid JSON was being passed to json_decode(): valid JSON input is unaffected and will continue to be parsed normally.

Stream wrappers now verify peer certificates and host names by default when using SSL/TLS

All encrypted client streams now enable peer verification by default. By default, this will use OpenSSL's default CA bundle to verify the peer certificate. In most cases, no changes will need to be made to communicate with servers with valid SSL certificates, as distributors generally configure OpenSSL to use known good CA bundles.

The default CA bundle may be overridden on a global basis by setting either the openssl.cafile or openssl.capath configuration setting, or on a per request basis by using the cafile or capath context options.

While not recommended in general, it is possible to disable peer certificate verification for a request by setting the verify_peer context option to false, and to disable peer name validation by setting the verify_peer_name context option to false.

GMP resources are now objects

GMP resources are now objects. The functional API implemented in the GMP extension has not changed, and code should run unmodified unless it checks explicitly for a resource using is_resource() or similar.

Mcrypt functions now require valid keys and IVs

mcrypt_encrypt(), mcrypt_decrypt(), mcrypt_cbc(), mcrypt_cfb(), mcrypt_ecb(), mcrypt_generic() and mcrypt_ofb() will no longer accept keys or IVs with incorrect sizes, and block cipher modes that require IVs will now fail if an IV isn't provided.

cURL file uploads

Uploads using the @file syntax now require CURLOPT_SAFE_UPLOAD to be set to false. CURLFile should be used instead.

PHP 5.5

Backward Incompatible Changes

Although most existing PHP 5 code should work without changes, please take note of some backward incompatible changes:

Windows XP and 2003 support dropped

Support for Windows XP and 2003 has been dropped. Windows builds of PHP now require Windows Vista or newer.

Case insensitivity no longer locale specific

All case insensitive matching for function, class and constant names is now performed in a locale independent manner according to ASCII rules. This improves support for languages using the Latin alphabet with unusual collating rules, such as Turkish and Azeri.

This may cause issues for code that uses case insensitive matches for non-ASCII characters in multibyte character sets (including UTF-8), such as accented characters in many European languages. If you have a non-English, non-ASCII code base, then you will need to test that you are not inadvertently relying on this behaviour before deploying PHP 5.5 to production systems.

pack() and unpack() changes

Changes were made to pack() and unpack() to make them more compatible with Perl:

  • pack() now supports the "Z" format code, which behaves identically to "a".
  • unpack() now support the "Z" format code for NULL padded strings, and behaves as "a" did in previous versions: it will strip trailing NULL bytes.
  • unpack() now keeps trailing NULL bytes when the "a" format code is used.
  • unpack() now strips all trailing ASCII whitespace when the "A" format code is used.

Writing backward compatible code that uses the "a" format code with unpack() requires the use of version_compare(), due to the backward compatibility break.

For example:

// Old code:
$data unpack('a5'$packed);

// New code:
if (version_compare(PHP_VERSION'5.5.0-dev''>=')) {
$data unpack('Z5'$packed);
} else {
$data unpack('a5'$packed);

json_encode() changes

When the value passed to json_encode() triggers a JSON encoding error, FALSE is returned instead of partial output, unless options contains JSON_PARTIAL_OUTPUT_ON_ERROR. See json_last_error() for the full list of reasons that can cause JSON encoding to fail. One of the potential failure reasons is that value contains strings containing invalid UTF-8.

self, parent and static are now always case insensitive

Prior to PHP 5.5, cases existed where the self, parent, and static keywords were treated in a case sensitive fashion. These have now been resolved, and these keywords are always handled case insensitively: SELF::CONSTANT is now treated identically to self::CONSTANT.

PHP logo GUIDs removed

The GUIDs that previously resulted in PHP outputting various logos have been removed. This includes the removal of the functions to return those GUIDs. The removed functions are:

Internal execution changes

Extension authors should note that the zend_execute() function can no longer be overridden, and that numerous changes have been made to the execute_data struct and related function and method handling opcodes.

Most extension authors are unlikely to be affected, but those writing extensions that hook deeply into the Zend Engine should read the notes on these changes.

PHP 5.4

Backward Incompatible Changes

Although most existing PHP 5 code should work without changes, please take note of some backward incompatible changes:

  • Safe mode is no longer supported. Any applications that rely on safe mode may need adjustment, in terms of security.
  • Magic quotes has been removed. Applications relying on this feature may need to be updated, to avoid security issues. get_magic_quotes_gpc() and get_magic_quotes_runtime() now always return FALSE. set_magic_quotes_runtime() raises an E_CORE_ERROR level error on trying to enable Magic quotes.
  • The register_globals and register_long_arrays php.ini directives have been removed.
  • The mbstring.script_encoding directive has been removed. Use zend.script_encoding instead.
  • Call-time pass by reference has been removed.
  • The break and continue statements no longer accept variable arguments (e.g., break 1 + foo() * $bar;). Static arguments still work, such as break 2;. As a side effect of this change break 0; and continue 0; are no longer allowed.
  • The default character set for htmlspecialchars(), htmlentities() and html_entity_decode() is now UTF-8, instead of ISO-8859-1. Note that changing your output charset via the default_charset configuration setting does not affect htmlspecialchars/htmlentities unless you are passing "" (an empty string) as the encoding parameter to your htmlspecialchars()/htmlentities()/html_entity_decode() calls. Generally we do not recommend doing this because you should be able to change your output charset without affecting the runtime charset used by these functions. The safest approach is to explicitly set the charset on each call to htmlspecialchars(), htmlentities() and html_entity_decode().
  • In the date and time extension, the timezone can no longer be set using the TZ environment variable. Instead you have to specify a timezone using the date.timezone php.ini option or date_default_timezone_set() function. PHP will no longer attempt to guess the timezone, and will instead fall back to "UTC" and issue a E_WARNING.
  • Non-numeric string offsets - e.g. $a['foo'] where $a is a string - now return false on isset() and true on empty(), and produce a E_WARNING if you try to use them. Offsets of types double, bool and null produce a E_NOTICE. Numeric strings (e.g. $a['2']) still work as before. Note that offsets like '12.3' and '5 foobar' are considered non-numeric and produce a E_WARNING, but are converted to 12 and 5 respectively, for backward compatibility reasons. Note: Following code returns different result. $str='abc';var_dump(isset($str['x'])); // false for PHP 5.4 or later, but true for 5.3 or less
  • Converting an array to a string will now generate an E_NOTICE level error, but the result of the cast will still be the string "Array".
  • Turning NULL, FALSE, or an empty string into an object by adding a property will now emit an E_WARNING level error, instead of E_STRICT.
  • Parameter names that shadow super globals now cause a fatal error. This prohibits code like function foo($_GET, $_POST) {}.
  • The Salsa10 and Salsa20 hash algorithms have been removed.
  • The Tiger hash algorithm now uses big-endian byte ordering. Please follow this example to write code that is compatible with both PHP 5.3 and 5.4.
  • array_combine() now returns array() instead of FALSE when two empty arrays are provided as parameters.
  • If you use htmlentities() with asian character sets, it works like htmlspecialchars() - this has always been the case in previous versions of PHP, but now an E_STRICT level error is emitted.
  • The third parameter of ob_start() has changed from boolean erase to integer flags. Note that code that explicitly set erase to FALSE will no longer behave as expected in PHP 5.4: please follow this example to write code that is compatible with PHP 5.3 and 5.4.

The following keywords are now reserved, and may not be used as names by functions, classes, etc.

The following functions have been removed from PHP:

PHP 5.3

Backward Incompatible Changes

Although most existing PHP 5 code should work without changes, please take note of some backward incompatible changes:

  • The newer internal parameter parsing API has been applied across all the extensions bundled with PHP 5.3.x. This parameter parsing API causes functions to return NULL when passed incompatible parameters. There are some exceptions to this rule, such as the get_class() function, which will continue to return FALSE on error.
  • clearstatcache() no longer clears the realpath cache by default.
  • realpath() is now fully platform-independent. Consequence of this is that invalid relative paths such as __FILE__ . "/../x" do not work anymore.
  • The call_user_func() family of functions now propagate $this even if the callee is a parent class.
  • The array functions natsort(), natcasesort(), usort(), uasort(), uksort(), array_flip(), and array_unique() no longer accept objects passed as arguments. To apply these functions to an object, cast the object to an array first.
  • The behaviour of functions with by-reference parameters called by value has changed. Where previously the function would accept the by-value argument, a fatal error is now emitted. Any previous code passing constants or literals to functions expecting references, will need altering to assign the value to a variable before calling the function.
  • The new mysqlnd library necessitates the use of MySQL 4.1's newer 41-byte password format. Continued use of the old 16-byte passwords will cause mysql_connect() and similar functions to emit the error, "mysqlnd cannot connect to MySQL 4.1+ using old authentication."
  • The new mysqlnd library does not read mysql configuration files (my.cnf/my.ini), as the older libmysqlclient library does. If your code relies on settings in the configuration file, you can load it explicitly with the mysqli_options() function. Note that this means the PDO specific constants PDO::MYSQL_ATTR_READ_DEFAULT_FILE and PDO::MYSQL_ATTR_READ_DEFAULT_GROUP are not defined if MySQL support in PDO is compiled with mysqlnd.
  • The trailing / has been removed from the SplFileInfo class and other related directory classes.
  • The __toString() magic method can no longer accept arguments.
  • The magic methods __get(), __set(), __isset(), __unset(), and __call() must always be public and can no longer be static. Method signatures are now enforced.
  • The __call() magic method is now invoked on access to private and protected methods.
  • func_get_arg(), func_get_args() and func_num_args() can no longer be called from the outermost scope of a file that has been included by calling include or require from within a function in the calling file.
  • An emulation layer for the MHASH extension to wrap around the Hash extension have been added. However not all the algorithms are covered, notable the s2k hashing algorithm. This means that s2k hashing is no longer available as of PHP 5.3.0.

The following keywords are now reserved and may not be used in function, class, etc. names.

PHP 5.2

Backward Incompatible Changes

Although most existing PHP 5 code should work without changes, you should pay attention to the following backward incompatible changes:

  • getrusage() returns NULL when passed incompatible arguments as of PHP 5.2.1.
  • ZipArchive::setCommentName() returns TRUE on success as of PHP 5.2.1.
  • ZipArchive::setCommentIndex() returns TRUE on success as of PHP 5.2.1.
  • SplFileObject::getFilename() returns the filename, not relative/path/to/file, as of PHP 5.2.1.
  • Changed priority of PHPRC environment variable on Win32 The PHPRC environment variable now takes priority over the path stored in the Windows registry.
  • CLI SAPI no longer checks cwd for php.ini or the php-cli.ini file In PHP 5.1.x an undocumented feature was added that made the CLI binary check the current working directory for a PHP configuration file, potentially leading to unpredictable behavior if an unexpected configuration file were read. This functionality was removed in 5.2.0, and PHP will no longer search CWD for the presence of php.ini or php-cli.ini files. See also the command line section of the manual.
  • Added a warning when performing modulus 0 operations In earlier versions of PHP, performing integer % 0 did not emit any warning messages, instead returning an unexpected return value of FALSE. As of PHP 5.2.0, this operation will emit an E_WARNING, as is the case in all other instances where division by zero is performed.
    print 10 0;
    /* Warning:  Division by zero in filename on line n */
  • Changed __toString() to be called wherever applicable. The magic method __toString() will now be called in a string context, that is, anywhere an object is used as a string. The fallback of returning a string that contains the object identifier was dropped in PHP 5.2.0. It became problematic because an object identifier cannot be considered unique. This change will mean that your application is flawed if you have relied on the object identifier as a return value. An attempt to use that value as a string will now result in a catchable fatal error.
    class foo {}
    $foo = new foo;
    /* Catchable fatal error:  Object of class foo could
       not be converted to string in filename on line n */
    Even with __toString(), objects cannot be used as array indices or keys. We may add built-in hash support for this at a later date, but as of PHP 5.2.x you will need to either provide your own hashing or use the new SPL function spl_object_hash(). Exceptions can not be thrown from __toString() methods.
    class foo {
        public function 
    __toString() {
            throw new 

    try {
        print new 
    /* Fatal error:  Method foo::__toString() must
           not throw an exception in filename on line n */
    } catch(Exception $e) {}
  • Dropped abstract static class functions. Due to an oversight, PHP 5.0.x and 5.1.x allowed abstract static functions in classes. As of PHP 5.2.x, only interfaces can have them.
    abstract class foo {
        abstract static function 
    /* Strict Standards:  Static function foo::bar()
           should not be abstract in filename on line n */
  • Oracle extension requires at least Oracle 10 on Windows.
  • Added RFC2397 (data: stream) support. The introduction of the 'data' URL scheme has the potential to lead to a change of behavior under Windows. If you are working with a NTFS file system and making use of meta streams in your application, and if you just happen to be using a file with the name 'data:' that is accessed without any path information - it won't work any more. The fix is to use the 'file:' protocol when accessing it. See also » RFC 2397
    /* when allow_url_include is OFF (default) */
    include "data:;base64,PD9waHAgcGhwaW5mbygpOz8+";
    /* Warning:  include(): URL file-access is disabled
       in the server configuration in filename on line n */
  • Regression in glob() patterns In version 5.2.4 a security fix caused a regression for patterns of the form "/foo/*/bar/*". Since version 5.2.5 instead of raising a warning the glob() function will return FALSE when openbase_dir restrictions are violated.

PHP 5.0

Backward Incompatible Changes

Although most existing PHP 4 code should work without changes, you should pay attention to the following backward incompatible changes:

  • There are some new reserved keywords.
  • strrpos() and strripos() now use the entire string as a needle.
  • Illegal use of string offsets causes E_ERROR instead of E_WARNING. An example illegal use is: $str = 'abc'; unset($str[0]);.
  • array_merge() was changed to accept only arrays. If a non-array variable is passed, a E_WARNING will be thrown for every such parameter. Be careful because your code may start emitting E_WARNING out of the blue.
  • PATH_TRANSLATED server variable is no longer set implicitly under Apache2 SAPI in contrast to the situation in PHP 4, where it is set to the same value as the SCRIPT_FILENAME server variable when it is not populated by Apache. This change was made to comply with the » CGI/1.1 specification. Please refer to » bug #23610 for further information, and see also the $_SERVER['PATH_TRANSLATED'] description in the manual. This issue also affects PHP versions >= 4.3.2.
  • The T_ML_COMMENT constant is no longer defined by the Tokenizer extension. If error_reporting is set to E_ALL, PHP will generate a notice. Although the T_ML_COMMENT was never used at all, it was defined in PHP 4. In both PHP 4 and PHP 5 // and /* */ are resolved as the T_COMMENT constant. However the PHPDoc style comments /** */, which starting PHP 5 are parsed by PHP, are recognized as T_DOC_COMMENT.
  • $_SERVER should be populated with argc and argv if variables_order includes "S". If you have specifically configured your system to not create $_SERVER, then of course it shouldn't be there. The change was to always make argc and argv available in the CLI version regardless of the variables_order setting. As in, the CLI version will now always populate the global $argc and $argv variables.
  • An object with no properties is no longer considered "empty".
  • In some cases classes must be declared before use. It only happens if some of the new features of PHP 5 (such as interfaces) are used. Otherwise the behaviour is the old.
  • get_class(), get_parent_class() and get_class_methods() now return the name of the classes/methods as they were declared (case-sensitive) which may lead to problems in older scripts that rely on the previous behaviour (the class/method name was always returned lowercased). A possible solution is to search for those functions in all your scripts and use strtolower(). This case sensitivity change also applies to the magical predefined constants __CLASS__, __METHOD__, and __FUNCTION__. The values are returned exactly as they're declared (case-sensitive).
  • ip2long() now returns FALSE when an invalid IP address is passed as argument to the function, and no longer -1.
  • If there are functions defined in the included file, they can be used in the main file independent if they are before return or after. If the file is included twice, PHP 5 issues fatal error because functions were already declared, while PHP 4 doesn't complain about it. It is recommended to use include_once instead of checking if the file was already included and conditionally return inside the included file.
  • include_once and require_once first normalize the path of included file on Windows so that including A.php and a.php include the file just once.
  • Passing an array to a function by value no longer resets the array's internal pointer for array accesses made within the function. In other words, in PHP 4 when you passed an array to a function, its internal pointer inside the function would be reset, while in PHP 5, when you pass an array to a function, its array pointer within the function will be wherever it was when the array was passed to the function.

Example #1 strrpos() and strripos() now use the entire string as a needle

var_dump(strrpos('ABCDEF','DEF')); //int(3)

var_dump(strrpos('ABCDEF','DAF')); //bool(false)

Example #2 An object with no properties is no longer considered "empty"

class test { }
$t = new test();

var_dump(empty($t)); // echo bool(false)

if ($t) {
// Will be executed

Example #3 In some cases classes must be declared before used

//works with no errors:
$a = new a();

//throws an error:
$a = new b();

implements {