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.
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 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.
<?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.
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.
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 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_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.
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.
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.
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:
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 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.
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.
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:
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.
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.
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 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 '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.
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.
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.
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().
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.