PHP 8.1.15 Released!

不向后兼容的变更

PHP 核心中不向后兼容的变更

字符串与数字的比较

数字与非数字形式的字符串之间的非严格比较现在将首先将数字转为字符串,然后比较这两个字符串。 数字与数字形式的字符串之间的比较仍然像之前那样进行。 请注意,这意味着 0 == "not-a-number" 现在将被认为是 false 。

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

其它不向后兼容的变更

  • match 现在是一个保留字。

  • 断言(Assertion)失败现在默认抛出异常。如果想要改回之前的行为,可以在 INI 设置中设置 assert.exception=0

  • mixed 现在是保留字,所以不能用于类,接口或者 trait,也禁止在命名空间中使用。

  • 与类名相同的方法名将不再被当做构造方法。应该使用__construct() 来取代它。

  • 不再允许通过静态调用的方式去调用非静态方法。因此is_callable()在检查一个类名与非静态方法 时将返回失败(应当检查一个类的实例)。

  • (real)(unset) 转换已被移除。

  • 移除 track_errors 执行。这意味着不能再用 php_errormsg。可以改用 error_get_last() 函数。

  • 移除定义不区分大小写的常量功能。define() 的第三个参数可能不再为 true

  • 移除使用 __autoload() 函数指定自动加载器的功能。应该改用 spl_autoload_register()

  • errcontext 参数将不再传递给使用 set_error_handler() 设置的自定义错误处理程序。

  • 移除 create_function()。应该改用匿名函数。

  • 移除 each()。应该改用 foreach 或者 ArrayIterator

  • 移除在方法中使用 Closure::fromCallable()ReflectionMethod::getClosure() 创建的匿名函数中解绑 this 的能力。

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

  • 移除对对象使用 array_key_exists() 的能力。应该改用 isset()property_exists()

  • array_key_exists()key 参数类型的行为已经和 isset() 和正常数组访问一致。所有的 key 类型现在使用通用的强制转换,数组/对象 key 会抛出 TypeError

  • 任意一个数组,将数字 n 作为第一个数字 key,下一个隐式键将会是 n+1。即使 n 为负数也是如此。

  • error_reporting 默认级别现在是 E_ALL。之前排除 E_NOTICEE_DEPRECATED

  • 现在默认启用 display_startup_errors

  • 在没有父级的类中使用 parent 将会导致 fatal compile-time 错误。

  • @ 操作将不再屏蔽 fatal 错误(E_ERRORE_CORE_ERRORE_COMPILE_ERRORE_USER_ERRORE_RECOVERABLE_ERRORE_PARSE)。当使用 @ 时,接受 error_reporting 为 0 的错误处理程序,应该调整为使用位掩码检查:

    <?php
    // 之前
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (
    error_reporting() == 0) {
    return
    false;
    }
    // ...
    }

    // 现在
    function my_error_handler($err_no, $err_msg, $filename, $linenum) {
    if (!(
    error_reporting() & $err_no)) {
    return
    false;
    }
    // ...
    }
    ?>

    此外,应注意在生产环境中不显示错误消息,这可能会导致信息泄露。确保 display_errors=Off 与错误记录一起使用。

  • #[ 不再解释为注释的开头,因为此语法现在用于注解。

  • 由于不兼容的方法签名(违反 LSP)导致的继承错误现在将始终生成致命错误。以前在某些情况下会生成警告。

  • 相对于位移、加法还有减法,连接运算符的优先级已经更改。

    <?php
    echo "Sum: " . $a + $b;
    // 之前解释为:
    echo ("Sum: " . $a) + $b;
    // 现在解释为:
    echo "Sum:" . ($a + $b);
    ?>

  • 在运行时默认值解析为 null 的参数,将不在默默将参数类型标记为可为 null。必须改用指定可为 null 类型或者默认值为 null

    <?php
    // 之前:
    function test(int $arg = CONST_RESOLVING_TO_NULL) {}
    // 之后:
    function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
    // 或者是
    function test(int $arg = null) {}
    ?>

  • 一些警告已转换为 Error 异常:

    • 尝试向非对象写入属性。之前会默默的为 null、false 和空字符串创建 stdClass 对象。
    • 尝试追加元素到已使用 PHP_INT_MAX 作为 key 的数组。
    • 尝试使用无效类型(array 或 object)作为数组的 key 或者字符串的 offset。
    • 尝试向标量值写入数组索引。
    • 尝试解包非数组或 Traversable。
    • 尝试访问未定义的常量,之前,访问未定义的常量将会导致警告并解释为字符串。

    一些通知已转换为警告:

    • 尝试读取未定义的变量。
    • 尝试读取未定义的属性。
    • 尝试读取未定义的数组 key。
    • 尝试读取非对象的属性。
    • 尝试读取非数组的数组索引。
    • 尝试转换数组为字符串。
    • 尝试使用资源作为数组 key。
    • 尝试使用 null、bool、float 作为字符串 offset。
    • 尝试读取越界的字符串 offset。
    • 尝试将空字符串分配给字符串 offset。

  • 尝试将多字节字符串分配给字符串 offset 现在将发出警告。

  • 源文件中的异常字符(比如字符串边界外的 NUL 字节)现在将导致 ParseError 异常而不是编译警告。

  • 未捕获异常现在会经过“clean shutdown”,这意味着未捕获的异常之后调用析构方法。

  • 编译时 fatal error“Only variables can be passed by reference”已延迟到运行时,并转换为“Argument cannot be passed by reference”Error异常。

  • 一些“Only variables should be passed by reference”通知已转换为“Argument cannot be passed by reference”异常。

  • 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 资源(resource)s 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.

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

DOM 扩展中没有实现行为的方法已被删除:

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

Enchant

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

  • 移除弃用函数 image2wbmp()

  • 移除弃用函数 png2wbmp()jpeg2wbmp()

  • 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

Internationalization Functions

  • The deprecated constant INTL_IDNA_VARIANT_2003 has been removed.

  • 移除弃用常量 Normalizer::NONE

LDAP

MBString

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

OpenSSL

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)

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:

    • 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

  • sem_get()auto_release 参数已从接受 int 更改为接受 bool。

Tidy

Tokenizer

  • T_COMMENT 记号将不再包含尾随的换行符。换行符将成为以下 T_WHITESPACE 记号的一部分。需要注意的是,T_COMMENT 后面并不总是跟空格,也可能跟着 T_CLOSE_TAG 或者文件结尾。

  • 命名空间名称现在使用 T_NAME_QUALIFIEDFoo\Bar)、T_NAME_FULLY_QUALIFIED\Foo\Bar) 和 T_NAME_RELATIVEnamespace\Foo\Bar)记号表示。 T_NS_SEPARATOR is only used for standalone namespace separators, and only syntactially valid in conjunction with group use declarations.

XMLReader

XMLReader::open()XMLReader::xml() 现在是静态方法。也可以作为实例方法调用,但如果继承类需要覆盖这些方法,要声明为 static。

XML-RPC

XML-RPC 扩展已移动到 PECL,不再是 PHP 发行版的一部分。

Zip

ZipArchive::OPSYS_Z_CPM 已移除(名字错误)。使用 ZipArchive::OPSYS_CPM 代替。

Zlib

Windows PHP 测试包

测试运行器从 run-test.php 重命名为 run-tests.php,以匹配其在 php-src 中的名字。

add a note

User Contributed Notes 2 notes

up
15
retry, abort, fail
11 months ago
The change in string to int comparison mentioned above (e.g. '' == 0 now equates to false) has some other nasty consequences:

$a = '';

// php 8

if ( $a < 0 ) echo 'true'; // echos true
if ( $a < -1) echo 'true'; // echos true
if ( $a < -100 ) echo 'true'; // echos true

// php 7

if ( $a < 0 ) echo 'true'; // no output
if ( $a < -1) echo 'true'; // no output
if ( $a < -100 ) echo 'true'; // no output

So in a situation where you may have a web form input and expected an empty value to equate to 0, watch out not only for == 0, != 0, and <= 0 comparisons, but ALL < or <= comparisons to negative integers.
up
-1
1035041238 at qq dot com
2 months ago
In PHP 8, an empty string is less than any number, an English letter is always bigger than any number.

More interesting are the punctuation marks and non-word characters, some bigger than numbers, some smaller than numbers

$string = '`~!@#$%^&*()-_=+[]{};:\'"\\|,.<>/?';

$number = 999999999999;

$str_len = strlen($string);

$bigger = $smaller = $equal = [];
for ( $i = 0; $i < $str_len; ++$i ) {
    if ( $string[$i] > $number ) {
        $bigger[] = $string[$i];
    } elseif ( $string[$i] < $number ) {
        $smaller[] = $string[$i];
    } else {
        $equal[] = $string[$i];
    }
}

var_dump( $bigger ); //['`', '~', '@', '^', '_', '=', '[', ']', '{', '}', ';', ':', '\', '|', '<', '>', '?']
var_dump( $smaller); //['!', '#', '$', '%', '&', '*', '(', ')', '-', '+', ''', '"', ',', '.', '/']
var_dump( $equal); //[]
To Top