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

  `<?php
  // Replace
  function my_error_handler($err_no, $err_msg, $filename, $linenum) {
  if (error_reporting() == 0) {
  return false;
  }
  // ...
  }
  // With
  function my_error_handler($err_no, $err_msg, $filename, $linenum) {
  if (!(error_reporting() & $err_no)) {
  return false;
  }
  // ...
  }
  ?>`

  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.

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

  `<?php
  // 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.
  - Passing invalid countable types to count() will throw a TypeError.

  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:

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

  `<?php
  class X {
  use T1, T2 {
  func as otherFunc;
  }
  function 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:

  `<?php
  trait MyTrait {
  abstract private function neededByTrait(): string;
  }
  class MyClass {
  use MyTrait;
  // 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.

  `<?php
  setlocale(LC_ALL, "de_DE");
  $f = 3.14;
  echo $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.

  `<?php
  // Instead of:
  $array{0};
  $array{"key"};
  // Write:
  $array[0];
  $array["key"];
  ?>`

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

- curl_init() will now return a CurlHandle object rather than a resource. The curl_close() function no longer has an effect, instead the CurlHandle instance is automatically destroyed if it is no longer referenced.

- curl_multi_init() will now return a CurlMultiHandle object rather than a resource. The curl_multi_close() function no longer has an effect, instead the CurlMultiHandle instance is automatically destroyed if it is no longer referenced.

- curl_share_init() will now return a CurlShareHandle object rather than a resource. The curl_share_close() function no longer has an effect, instead the CurlShareHandle instance is automatically destroyed if it is no longer referenced.

- enchant_broker_init() will now return an EnchantBroker object rather than a resource.

- enchant_broker_request_dict() and enchant_broker_request_pwl_dict() will now return an EnchantDictionary object rather than a resource.

- The GD extension now uses GdImage objects as the underlying data structure for images, rather than resources. The imagedestroy() function no longer has an effect; instead the GdImage instance is automatically destroyed if it is no longer referenced.

- openssl_x509_read() and openssl_csr_sign() will now return an OpenSSLCertificate object rather than a resource. The openssl_x509_free() function is deprecated and no longer has an effect, instead the OpenSSLCertificate instance is automatically destroyed if it is no longer referenced.

- openssl_csr_new() will now return an OpenSSLCertificateSigningRequest object rather than a resource.

- openssl_pkey_new() will now return an OpenSSLAsymmetricKey object rather than a resource. The openssl_pkey_free() function is deprecated and no longer has an effect, instead the OpenSSLAsymmetricKey instance is automatically destroyed if it is no longer referenced.

- shmop_open() will now return a Shmop object rather than a resource. The shmop_close() function no longer has an effect, and is deprecated; instead the Shmop instance is automatically destroyed if it is no longer referenced.

- socket_create(), socket_create_listen(), socket_accept(), socket_import_stream(), socket_addrinfo_connect(), socket_addrinfo_bind(), and socket_wsaprotocol_info_import() will now return a Socket object rather than a resource. socket_addrinfo_lookup() will now return an array of AddressInfo objects rather than resources.

- msg_get_queue() will now return an SysvMessageQueue object rather than a resource.

- sem_get() will now return an SysvSemaphore object rather than a resource.

- shm_attach() will now return an SysvSharedMemory object rather than a resource.

- xml_parser_create() and xml_parser_create_ns() will now return an XMLParser object rather than a resource. The xml_parser_free() function no longer has an effect, instead the XMLParser instance is automatically destroyed if it is no longer referenced.

- The XMLWriter functions now accept and return, respectively, XMLWriter objects instead of resources.

- inflate_init() will now return an InflateContext object rather than a resource.

- deflate_init() will now return a DeflateContext object rather than a resource.

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

### CURL

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

### DOM

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()

### Enchant

- enchant_broker_list_dicts(), enchant_broker_describe() and enchant_dict_suggest() will now return an empty array instead of `null`.

### Exif

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

### Filter

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

### GD

- 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

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

### Iconv

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

### IMAP

- The unused `default_host` argument of imap_headerinfo() has been removed.

- The imap_header() function which is an alias of imap_headerinfo() has been removed.

### Internationalization Functions

- The deprecated constant `INTL_IDNA_VARIANT_2003` has been removed.

- The deprecated `Normalizer::NONE` constant has been removed.

### LDAP

- The deprecated functions ldap_sort(), ldap_control_paged_result() and ldap_control_paged_result_response() have been removed.

- The interface of ldap_set_rebind_proc() has changed; the `callback` parameter does not accept empty strings anymore; `null` should be used instead.

### MBString

- The mbstring.func_overload directive has been removed. The related `MB_OVERLOAD_MAIL`, `MB_OVERLOAD_STRING`, and `MB_OVERLOAD_REGEX` constants have also been removed. Finally, the `"func_overload"` and `"func_overload_list"` entries in mb_get_info() have been removed.

- mb_parse_str() can no longer be used without specifying a result array.

- A number of deprecated mbregex aliases have been removed. See the following list for which functions should be used instead:

  - mbregex_encoding() → mb_regex_encoding()
  - mbereg() → mb_ereg()
  - mberegi() → mb_eregi()
  - mbereg_replace() → mb_ereg_replace()
  - mberegi_replace() → mb_eregi_replace()
  - mbsplit() → mb_split()
  - mbereg_match() → mb_ereg_match()
  - mbereg_search() → mb_ereg_search()
  - mbereg_search_pos() → mb_ereg_search_pos()
  - mbereg_search_regs() → mb_ereg_search_regs()
  - mbereg_search_init() → mb_ereg_search_init()
  - mbereg_search_getregs() → mb_ereg_search_getregs()
  - mbereg_search_getpos() → mb_ereg_search_getpos()
  - mbereg_search_setpos() → mb_ereg_search_setpos()

- The `e` modifier for mb_ereg_replace() has been removed. mb_ereg_replace_callback() should be used instead.

- A non-string pattern argument to mb_ereg_replace() will now be interpreted as a string instead of an ASCII codepoint. The previous behavior may be restored with an explicit call to chr().

- The `needle` argument for mb_strpos(), mb_strrpos(), mb_stripos(), mb_strripos(), mb_strstr(), mb_stristr(), mb_strrchr() and mb_strrichr() can now be empty.

- The `is_hex` parameter, which was not used internally, has been removed from mb_decode_numericentity().

- The legacy behavior of passing the encoding as the third argument instead of an offset for the mb_strrpos() function has been removed; an explicit `0` offset with the encoding should be provided as the fourth argument instead.

- The `ISO_8859-*` character encoding aliases have been replaced by `ISO8859-*` aliases for better interoperability with the iconv extension. The mbregex ISO 8859 aliases with underscores (`ISO_8859_*` and `ISO8859_*`) have also been removed.

- mb_ereg() and mb_eregi() will now return boolean `true` on a successful match. Previously they returned integer `1` if `matches` was not passed, or `max(1, strlen($matches[0]))` if `matches` was passed.

### OCI8

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

### ODBC

- odbc_connect() no longer reuses connections.

- The unused `flags` parameter of odbc_exec() has been removed.

### OpenSSL

- openssl_seal() and openssl_open() now require `method` to be passed, as the previous default of `"RC4"` is considered insecure.

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

### PDO ODBC

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

### PDO MySQL

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.

### PostgreSQL

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

### Phar

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

### Reflection

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

### Sockets

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

- SplFileObject::fgetss() has been removed.

- SplFileObject::seek() now always seeks to the beginning of the line. Previously, positions `>=1` sought to the beginning of the next line.

- SplHeap::compare() now specifies a method signature. Inheriting classes implementing this method will now have to use a compatible method signature.

- SplDoublyLinkedList::push(), SplDoublyLinkedList::unshift() and SplQueue::enqueue() now return void instead of `true`.

- spl_autoload_register() will now always throw a TypeError on invalid arguments, therefore the second argument `do_throw` is ignored and a notice will be emitted if it is set to `false`.

- SplFixedArray is now an IteratorAggregate and not an Iterator. SplFixedArray::rewind(), SplFixedArray::current(), SplFixedArray::key(), SplFixedArray::next(), and SplFixedArray::valid() have been removed. In their place, SplFixedArray::getIterator() has been added. Any code which uses explicit iteration over SplFixedArray must now obtain an Iterator through SplFixedArray::getIterator(). This means that SplFixedArray is now safe to use in nested loops.

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

  - hebrevc()
  - convert_cyr_string()
  - money_format()
  - ezmlm_hash()
  - restore_include_path()
  - get_magic_quotes_gpc()
  - get_magic_quotes_runtime()
  - fgetss()

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

  - `http://example.com/foo → query = null, fragment = null`
  - `http://example.com/foo? → query = "", fragment = null`
  - `http://example.com/foo# → query = null, fragment = ""`
  - `http://example.com/foo?# → 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.

  `<?php
  $ctx = stream_context_create(['http' => ['protocol_version' => '1.0']]);
  echo file_get_contents('http://example.org', 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).

### Sysvsem

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

### Tidy

- The `use_include_path` parameter, which was not used internally, has been removed from tidy_repair_string().

- tidy::repairString() and tidy::repairFile() became static methods.

### Tokenizer

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

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.

### XML-RPC

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

### Zip

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

### Zlib

- gzgetss() has been removed.

- zlib.output_compression is no longer automatically disabled for `Content-Type: image/*`.

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

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open