[doc-en] master: PHP 8.6 migration guide (#5900)
| From: | Jordi Kroon via GitHub | Date: | Tue, 13 Oct 2026 17:01:09 +0000 |
| Subject: | [doc-en] master: PHP 8.6 migration guide (#5900) | ||
| Groups: | php.doc.cvs | ||
| Request: | Send a blank email to doc-cvs+get-23841@lists.php.net to get a copy of this message | ||
Author: Jordi Kroon (jordikroon)
Committer: GitHub (web-flow)
Pusher: jordikroon
Date: 2026-10-13T19:01:05+02:00
Commit: https://github.com/php/doc-en/commit/894fa67ba030cd241a56d898869852c7b1a59c0a
Raw diff: https://github.com/php/doc-en/commit/894fa67ba030cd241a56d898869852c7b1a59c0a.diff
PHP 8.6 migration guide (#5900)
* Added initial files for 8.6 migration
* Document new classes & functions
* Document deprecations
* Document Windows changes
* Document Constant changes
* Document new features
* Document php-src#c01c9d009c1db68a87dabd45b128f544913a63f2
Changed paths:
A appendices/migration86.xml
A appendices/migration86/constants.xml
A appendices/migration86/deprecated.xml
A appendices/migration86/incompatible.xml
A appendices/migration86/new-classes.xml
A appendices/migration86/new-features.xml
A appendices/migration86/new-functions.xml
A appendices/migration86/other-changes.xml
A appendices/migration86/windows-support.xml
M manual.xml
Diff:
diff --git a/appendices/migration86.xml b/appendices/migration86.xml
new file mode 100644
index 000000000000..89cdef36774f
--- /dev/null
+++ b/appendices/migration86.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appendix xml:id="migration86" xmlns="http://docbook.org/ns/docbook">
+ <title>Migrating from PHP 8.5.x to PHP 8.6.x</title>
+
+ <simpara>
+ This new minor version brings with it a number of
+ <link linkend="migration86.new-features">new features</link> and a
+ <link linkend="migration86.incompatible">few incompatibilities</link>
+ that should be tested for before switching PHP versions in production
+ environments.
+ </simpara>
+
+ <simpara>
+ &manual.migration.seealso;
+ <link linkend="migration71">7.1.x</link>,
+ <link linkend="migration72">7.2.x</link>,
+ <link linkend="migration73">7.3.x</link>,
+ <link linkend="migration74">7.4.x</link>,
+ <link linkend="migration80">8.0.x</link>,
+ <link linkend="migration81">8.1.x</link>,
+ <link linkend="migration82">8.2.x</link>,
+ <link linkend="migration83">8.3.x</link>,
+ <link linkend="migration84">8.4.x</link>,
+ <link linkend="migration85">8.5.x</link>.
+ </simpara>
+
+ &appendices.migration86.new-features;
+ &appendices.migration86.new-classes;
+ &appendices.migration86.new-functions;
+ &appendices.migration86.constants;
+ &appendices.migration86.incompatible;
+ &appendices.migration86.deprecated;
+ &appendices.migration86.other-changes;
+ &appendices.migration86.windows-support;
+
+</appendix>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/constants.xml b/appendices/migration86/constants.xml
new file mode 100644
index 000000000000..c3d4ced715a0
--- /dev/null
+++ b/appendices/migration86/constants.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.constants">
+ <title>New Global Constants</title>
+
+ <sect2 xml:id="migration86.constants.core">
+ <title>Core</title>
+
+ <simplelist>
+ <member>
+ <constant>ARRAY_FILTER_USE_VALUE</constant>
+ </member>
+ <member>
+ <constant>STREAM_CRYPTO_STATUS_NONE</constant>
+ </member>
+ <member>
+ <constant>STREAM_CRYPTO_STATUS_WANT_READ</constant>
+ </member>
+ <member>
+ <constant>STREAM_CRYPTO_STATUS_WANT_WRITE</constant>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.constants.curl">
+ <title>cURL</title>
+
+ <simplelist>
+ <member>
+ <constant>CURLINFO_SIZE_DELIVERED</constant> (libcurl >= 8.20.0)
+ </member>
+ <member>
+ <constant>CURLOPT_SEEKFUNCTION</constant>
+ </member>
+ <member>
+ <constant>CURL_SEEKFUNC_OK</constant>
+ </member>
+ <member>
+ <constant>CURL_SEEKFUNC_FAIL</constant>
+ </member>
+ <member>
+ <constant>CURL_SEEKFUNC_CANTSEEK</constant>
+ </member>
+ <member>
+ <constant>CURL_READFUNC_ABORT</constant>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.constants.mysqli">
+ <title>MySQLi</title>
+
+ <simplelist>
+ <member>
+ <constant>MYSQLI_OPT_COMPRESS</constant>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.constants.openssl">
+ <title>OpenSSL</title>
+
+ <simplelist>
+ <member><constant>OPENSSL_RSA_PSS_SALTLEN_DIGEST</constant></member>
+ <member><constant>OPENSSL_RSA_PSS_SALTLEN_AUTO</constant></member>
+ <member><constant>OPENSSL_RSA_PSS_SALTLEN_MAX</constant></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.constants.sockets">
+ <title>Sockets</title>
+
+ <simplelist>
+ <member>
+ <constant>TCP_USER_TIMEOUT</constant> (Linux only)
+ </member>
+ <member>
+ <constant>AF_UNSPEC</constant>
+ </member>
+ <member>
+ <constant>EAI_BADFLAGS</constant>
+ </member>
+ <member>
+ <constant>EAI_NONAME</constant>
+ </member>
+ <member>
+ <constant>EAI_AGAIN</constant>
+ </member>
+ <member>
+ <constant>EAI_FAIL</constant>
+ </member>
+ <member>
+ <constant>EAI_NODATA</constant>
+ </member>
+ <member>
+ <constant>EAI_FAMILY</constant>
+ </member>
+ <member>
+ <constant>EAI_SOCKTYPE</constant>
+ </member>
+ <member>
+ <constant>EAI_SERVICE</constant>
+ </member>
+ <member>
+ <constant>EAI_ADDRFAMILY</constant>
+ </member>
+ <member>
+ <constant>EAI_SYSTEM</constant>
+ </member>
+ <member>
+ <constant>EAI_OVERFLOW</constant>
+ </member>
+ <member>
+ <constant>EAI_INPROGRESS</constant>
+ </member>
+ <member>
+ <constant>EAI_CANCELED</constant>
+ </member>
+ <member>
+ <constant>EAI_NOTCANCELED</constant>
+ </member>
+ <member>
+ <constant>EAI_ALLDONE</constant>
+ </member>
+ <member>
+ <constant>EAI_INTR</constant>
+ </member>
+ <member>
+ <constant>EAI_IDN_ENCODE</constant>
+ </member>
+ <member>
+ <constant>SO_DETACH_REUSEPORT_BPF</constant> (Linux only)
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.constants.sodium">
+ <title>Sodium</title>
+
+ <simplelist>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_BYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_KEYBYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_ND_KEYBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_ND_TWEAKBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_ND_INPUTBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_ND_OUTPUTBYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_NDX_KEYBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_NDX_TWEAKBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_NDX_INPUTBYTES</constant></member>
+
<member><constant>SODIUM_CRYPTO_IPCRYPT_NDX_OUTPUTBYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_PFX_KEYBYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_IPCRYPT_PFX_BYTES</constant></member>
+ <member><constant>SODIUM_CRYPTO_KEM_PUBLICKEYBYTES</constant> (libsodium >=
1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_SECRETKEYBYTES</constant> (libsodium >=
1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_CIPHERTEXTBYTES</constant> (libsodium
>= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_SHAREDSECRETBYTES</constant> (libsodium
>= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_SEEDBYTES</constant> (libsodium >=
1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_KEYPAIRBYTES</constant> (libsodium >=
1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_PUBLICKEYBYTES</constant>
(libsodium >= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_SECRETKEYBYTES</constant>
(libsodium >= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_CIPHERTEXTBYTES</constant>
(libsodium >= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_SHAREDSECRETBYTES</constant>
(libsodium >= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_SEEDBYTES</constant> (libsodium
>= 1.0.22)</member>
+ <member><constant>SODIUM_CRYPTO_KEM_MLKEM768_KEYPAIRBYTES</constant>
(libsodium >= 1.0.22)</member>
+ </simplelist>
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/deprecated.xml b/appendices/migration86/deprecated.xml
new file mode 100644
index 000000000000..2ce6ce2a5322
--- /dev/null
+++ b/appendices/migration86/deprecated.xml
@@ -0,0 +1,612 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.deprecated">
+ <title>Deprecated Features</title>
+
+ <sect2 xml:id="migration86.deprecated.core">
+ <title>PHP Core</title>
+
+ <sect3 xml:id="migration86.deprecated.core.namespace-as-class-constant-name">
+ <title>Using "namespace" as a class constant name</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_using_namespace_as_a_class_constant_name
-->
+
+ <simpara>
+ Declaring a <link linkend="language.oop5.constants">class constant</link>
+ named <literal>namespace</literal> is now deprecated.
+ <literal>namespace</literal> is a semi-reserved keyword, and the name is
+ being reserved for possible future syntax such as a
+ <literal>::namespace</literal> pseudo-constant analogous to
+ <literal>::class</literal>.
+ Methods and static properties named <literal>namespace</literal> are
+ not affected.
+ Rename the constant, for example to <literal>NAMESPACE_NAME</literal>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.return-in-finally">
+ <title>Returning from a finally block</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_returning_from_a_finally_block
-->
+
+ <simpara>
+ Using the <literal>return</literal> statement inside a &finally; block
+ is now deprecated, and emits a compile-time deprecation notice.
+ Such a <literal>return</literal> silently discards an exception thrown
+ by, or overwrites the value returned from, the associated
+ <literal>try</literal> or <literal>catch</literal> block, so failures
+ vanish without a trace.
+ Move the <literal>return</literal> out of the
<literal>finally</literal>
+ block instead; the same result can always be expressed without it.
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+function getConfig(): array
+{
+ try {
+ return loadConfig(); // throws if the file is missing
+ } finally {
+ return []; // Deprecated: the exception is gone, the caller just gets []
+ }
+}
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.nullable-debuginfo-return-type">
+ <title>Nullable return type for __debugInfo()</title>
+
+ <simpara>
+ Declaring a return type of
+ <type
class="union"><type>array</type><type>null</type></type>
for
+ <link linkend="language.oop5.magic.debuginfo">__debugInfo()</link> is now
+ deprecated.
+ Returning &null; from this method was
+ <link linkend="migration85.deprecated.core.return-null-from-debuginfo">already
+ deprecated in PHP 8.5</link>, so the nullable return type no longer
+ describes valid behaviour.
+ Declare <type>array</type> instead.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.return-value-from-constructor">
+ <title>Returning values from __construct() and __destruct()</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecate-return-value-from-construct
-->
+
+ <simpara>
+ Returning a value from
+ <link linkend="language.oop5.decon.constructor">__construct()</link> or
+ <link linkend="language.oop5.decon.destructor">__destruct()</link> is now
+ deprecated, and emits a compile-time deprecation notice.
+ These methods cannot declare a return type, and their return value can
+ never be observed during normal use, which made returning a value
+ confusing at best.
+ Using <literal>return;</literal> without a value to skip the remaining
+ logic is still allowed, as is calling these methods manually.
+ </simpara>
+
+ <simpara>
+ For the same reason, using <literal>yield</literal> to make
+ <link linkend="language.oop5.decon.constructor">__construct()</link> or
+ <link linkend="language.oop5.decon.destructor">__destruct()</link> a
+ <link linkend="language.generators">generator</link> is now deprecated.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.function-named-readonly">
+ <title>Naming a function readonly</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_the_possibility_to_name_a_function_readonly
-->
+
+ <simpara>
+ Declaring a function named <literal>readonly</literal> is now deprecated.
+ <link
linkend="language.oop5.properties.readonly-properties">readonly</link>
+ is a reserved keyword; a lexer exception was added in PHP 8.1 solely to
+ keep an existing WordPress function of that name working, and no other
+ keyword in PHP receives such special treatment.
+ Rename the function.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.reserved-identifiers">
+ <title>Using let, is, or _ as an identifier</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_using_let_as_an_identifier
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_using_is_as_an_identifier
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_using_as_a_constant_and_compile_time_alias
-->
+ <!-- Not listed in UPGRADING; implemented in php-src commits 0beb2a99d3 and 938a3110bf on the
PHP-8.6 branch -->
+
+ <simpara>
+ Using <literal>let</literal> or <literal>is</literal> as the name of a
+ class, interface, trait, enum, function, or constant (including via
+ <function>define</function>) is now deprecated, as is using either as
+ the alias in a <function>class_alias</function> call or in a
+ <literal>use</literal>, <literal>use function</literal>, or
+ <literal>use const</literal> declaration.
+ The names are being reserved as keywords for potential future features:
+ <literal>let</literal> for block scoping and <literal>is</literal> for
+ pattern matching.
+ Rename the affected symbols.
+ </simpara>
+
+ <simpara>
+ The
+ <link linkend="migration84.deprecated.core.underscore-class-name">deprecation
+ of <literal>_</literal> as a class name in PHP 8.4</link> is extended to
+ constants: declaring a constant named <literal>_</literal> with
+ <literal>const</literal> or <function>define</function>, and aliasing a
+ class or constant to <literal>_</literal> in a <literal>use</literal>
+ declaration, are now deprecated.
+ Functions named <literal>_</literal>, including the
+ <function>_</function> alias of <function>gettext</function>, are not
+ affected.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.define-case-insensitive">
+ <title>Third argument to define()</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_define_with_case_insensitive_being_specified
-->
+
+ <simpara>
+ Passing the <parameter>case_insensitive</parameter> argument to
+ <function>define</function> is now deprecated.
+ Case-insensitive constants were removed in PHP 8.0, since when the
+ argument has been ignored.
+ Explicitly passing &false; now emits a deprecation notice.
+ Passing &true; continues to emit a warning, whose message now states
+ that this will become an error in PHP 9.0.
+ Omit the argument.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.is-a-with-string">
+ <title>is_a() and is_subclass_of() with a string when allow_string is false</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_a_with_string_when_allow_string_is_false
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_subclass_of_with_string_when_allow_string_is_false
-->
+
+ <simpara>
+ Calling <function>is_a</function> or
<function>is_subclass_of</function>
+ with a string as the first argument while
+ <parameter>allow_string</parameter> is &false; is now deprecated.
+ In that case the functions always return &false;, even when the class
+ is already loaded, so such a call indicates a bug.
+ To avoid triggering autoloading, check <function>class_exists</function>
+ with autoloading disabled first, and then call the function with
+ <parameter>allow_string</parameter> set to &true;.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.metaphone">
+ <title>metaphone()</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_metaphone_function
-->
+
+ <simpara>
+ The <function>metaphone</function> function is now deprecated.
+ It implements the original 1990 Metaphone algorithm, which is limited to
+ English pronunciation rules and has long been superseded by Double
+ Metaphone and Metaphone 3.
+ Phonetic algorithms are language-specific and frequently updated, so
+ they are better served by a userland library; use one instead.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.php-filter-max-filter-count">
+ <title>More than 16 filters in a php://filter URL</title>
+ <!-- RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains
-->
+
+ <simpara>
+ Using more than 16 filters in a
+ <link linkend="wrappers.php">php://filter</link> URL without configuring
+ the <literal>filter.max_filter_count</literal> stream context option now
+ emits a deprecation notice.
+ Long filter chains have no known legitimate use, but are the basis of
+ a class of attacks that turn a local file inclusion or an arbitrary file
+ read into remote code execution.
+ When <literal>filter.max_filter_count</literal> is set explicitly,
+ exceeding it is already an error: opening the stream fails with a
+ warning.
+ </simpara>
+
+ <simpara>
+ Code that needs more filters should either add them with
+ <function>stream_filter_append</function> or
+ <function>stream_filter_prepend</function>, which are not limited, or
+ raise the limit through the context option, for example globally with
+ <function>stream_context_set_default</function>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.passing-objects-as-arrays">
+ <title>Passing objects to array_walk(), array_walk_recursive(), and
http_build_query()</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_array_parameter_of_array_walk_and_array_walk_recursive
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_data_parameter_of_http_build_query
-->
+
+ <simpara>
+ Passing an object as the <parameter>array</parameter> argument to
+ <function>array_walk</function> or
+ <function>array_walk_recursive</function> is now deprecated.
+ A small number of functions silently interpret an object as the array
+ of its visible properties.
+ Modifying such properties through a by-reference callback violates
+ <link
linkend="language.oop5.properties.readonly-properties">readonly</link>
+ semantics and can corrupt enum cases, and
+ <function>array_walk_recursive</function> does not recurse into nested
+ objects anyway.
+ Call <function>get_object_vars</function> on the object first and pass
+ the resulting array.
+ </simpara>
+
+ <simpara>
+ Passing an object as the <parameter>data</parameter> argument to
+ <function>http_build_query</function> is now deprecated.
+ Objects encountered as values within <parameter>data</parameter> now
+ also emit a deprecation notice when they are interpreted as arrays.
+ Treating objects as arrays has been a recurring source of confusion,
+ in particular for <interfacename>Stringable</interfacename> objects,
+ which users expect to be converted to a string instead.
+ Convert objects to arrays with <function>get_object_vars</function>,
+ recursively if necessary, before calling the function.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.type-function-aliases">
+ <title>is_double(), is_long(), is_integer(), and doubleval()</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_double
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_integer
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_is_long
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_doubleval
-->
+
+ <para>
+ Following the
+ <link
linkend="migration85.deprecated.core.non-canonical-cast-names">deprecation
+ of the non-canonical cast names in PHP 8.5</link>, the function aliases
+ that carry the same outdated type names are now deprecated.
+ They behave identically to their canonical counterparts, which should
+ be used instead:
+
+ <simplelist>
+ <member><function>is_double</function> =>
<function>is_float</function></member>
+ <member><function>is_long</function> =>
<function>is_int</function></member>
+ <member><function>is_integer</function> =>
<function>is_int</function></member>
+ <member><function>doubleval</function> =>
<function>floatval</function></member>
+ </simplelist>
+ </para>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.deprecated.core.locale-string-comparison">
+ <title>strcoll() and SORT_LOCALE_STRING</title>
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_strcoll
-->
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_sort_locale_string_flag_for_sort_functions
-->
+
+ <simpara>
+ The <function>strcoll</function> function is now deprecated.
+ It compares strings according to the <literal>LC_COLLATE</literal>
+ setting of the current locale, which is global process state set via
+ <function>setlocale</function> that any library can change, it is not
+ binary safe unlike every other string comparison function, and some
+ locales do not implement correct collation rules.
+ Use <methodname>Collator::compare</methodname> from the
+ <link linkend="book.intl">intl</link> extension instead, which is
+ accurate and has up-to-date collation rules.
+ </simpara>
+
+ <para>
+ The <constant>SORT_LOCALE_STRING</constant> flag of the sort functions
+ is now deprecated for the same reasons, as it sorts using
+ <function>strcoll</function>.
+ Use one of the following methods of the
+ <classname>Collator</classname> class instead:
+
+ <simplelist>
+ <member><methodname>Collator::sort</methodname></member>
+ <member><methodname>Collator::asort</methodname></member>
+ <member><methodname>Collator::sortWithSortKeys</methodname></member>
+ </simplelist>
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.bzip2">
+ <title>Bzip2</title>
+
+ <simpara>
+ Passing an object as the parameters of the
+ <link linkend="filters.compression">bzip2.compress and
+ bzip2.decompress</link> stream filters is now deprecated, as only an
+ associative array is documented and supported.
+ Call <function>get_object_vars</function> on the object first and pass
+ the resulting array.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_as_parameters_to_the_bzip2decompress_and_bzip2compress_stream_filters
-->
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.gmp">
+ <title>GMP</title>
+
+ <simpara>
+ The shift (<literal><<</literal>,
<literal>>></literal>) and
+ exponentiation (<literal>**</literal>) operators on
+ <classname>GMP</classname> objects now emit a deprecation notice when the
+ right operand is a <type>float</type> whose conversion to
+ <type>int</type> loses precision, consistent with the deprecation of
+ implicit lossy float to int conversions elsewhere in the language.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.mbstring">
+ <title>MBString</title>
+
+ <para>
+ The mbregex functions have been deprecated, because the underlying
+ Oniguruma library is no longer maintained; its maintenance ended in
+ April 2025.
+ The functions will be removed in PHP 9.0.
+ For most use cases the <link linkend="book.pcre">PCRE</link> functions
+ with the <literal>u</literal> modifier are a suitable replacement.
+ The affected functions are:
+ <!-- RFC: https://wiki.php.net/rfc/eol-oniguruma -->
+
+ <simplelist>
+ <member><function>mb_ereg</function></member>
+ <member><function>mb_ereg_match</function></member>
+ <member><function>mb_ereg_replace</function></member>
+ <member><function>mb_ereg_replace_callback</function></member>
+ <member><function>mb_ereg_search</function></member>
+ <member><function>mb_ereg_search_getpos</function></member>
+ <member><function>mb_ereg_search_getregs</function></member>
+ <member><function>mb_ereg_search_init</function></member>
+ <member><function>mb_ereg_search_pos</function></member>
+ <member><function>mb_ereg_search_regs</function></member>
+ <member><function>mb_ereg_search_setpos</function></member>
+ <member><function>mb_eregi</function></member>
+ <member><function>mb_eregi_replace</function></member>
+ <member><function>mb_regex_encoding</function></member>
+ <member><function>mb_regex_set_options</function></member>
+ <member><function>mb_split</function></member>
+ </simplelist>
+ </para>
+
+ <simpara>
+ The <constant>MB_ONIGURUMA_VERSION</constant> constant and the
+ <link
linkend="ini.mbstring.regex-retry-limit">mbstring.regex_retry_limit</link>
+ and
+ <link
linkend="ini.mbstring.regex-stack-limit">mbstring.regex_stack_limit</link>
+ INI directives are deprecated along with them.
+ </simpara>
+
+ <simpara>
+ Passing an object as the <parameter>vars</parameter> argument to
+ <function>mb_convert_variables</function> is now deprecated, as objects
+ are silently interpreted as the array of their visible properties.
+ Call <function>get_object_vars</function> on the object first, or use
+ <function>mb_convert_encoding</function>, which converts arrays
+ recursively and returns the result instead of modifying its argument
+ in place.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_vars_parameter_of_mb_convert_variables
-->
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.mysqli">
+ <title>MySQLi</title>
+
+ <simpara>
+ The <function>mysqli_get_charset</function> function and the
+ <methodname>mysqli::get_charset</methodname> method are now deprecated,
+ without replacement.
+ They expose internal implementation details of the character set that
+ have been meaningless since the switch to mysqlnd; in particular the
+ reported collation does not correspond to the collation defined by the
+ server.
+ To get the name of the current character set, use
+ <function>mysqli_character_set_name</function> or
+ <methodname>mysqli::character_set_name</methodname>.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_mysqli_get_charset
-->
+ </simpara>
+
+ <simpara>
+ The <function>mysqli_stmt_init</function> function and the
+ <methodname>mysqli::stmt_init</methodname> method are now deprecated.
+ They create an empty statement object that still has to be prepared
+ before use, and are a source of statement objects left in an
+ inconsistent state.
+ Use <function>mysqli_prepare</function> or
+ <methodname>mysqli::prepare</methodname> instead, which prepare the
+ statement in one step.
+ For the same reason, calling the
+ <methodname>mysqli_stmt::__construct</methodname> constructor without the
+ <parameter>query</parameter> parameter is now deprecated; pass the query
+ to the constructor instead.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_mysqlistmt_init
-->
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.reflection">
+ <title>Reflection</title>
+
+ <simpara>
+ Calling <methodname>ReflectionProperty::setValue</methodname> or
+ <methodname>ReflectionProperty::setRawValue</methodname> with an object
+ that is not an instance of the class on which the property was declared
+ is now deprecated.
+ Unlike the other <classname>ReflectionProperty</classname> methods, which
+ throw for such objects, these methods silently set a property on the
+ unrelated object, which is no better than a normal assignment and
+ conceptually wrong.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_reflectionpropertysetvalue_and_reflectionpropertysetrawvalue_with_wrong_types
-->
+ </simpara>
+
+ <simpara>
+ Passing an object to <methodname>ReflectionMethod::invoke</methodname> or
+ <methodname>ReflectionMethod::invokeArgs</methodname> when invoking a
+ static method is now deprecated.
+ Static methods have no <varname>$this</varname>, so the object was
+ ignored entirely and never validated.
+ Pass &null; instead.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_reflectionmethodinvoke_and_reflectionmethodinvokeargs_with_objects_for_static_methods
-->
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.session">
+ <title>Session</title>
+
+ <simpara>
+ Passing an object that does not implement both the
+ <methodname>SessionIdInterface::create_sid</methodname> and
+ <methodname>SessionUpdateTimestampHandlerInterface::validateId</methodname>
+ methods to <function>session_set_save_handler</function> is now
+ deprecated, regardless of which interfaces the object declares.
+ These methods are required for a save handler to behave correctly under
+ <link
linkend="ini.session.use-strict-mode">session.use_strict_mode</link>,
+ which is
+ <link linkend="migration86.incompatible.session">enabled by default as of
+ PHP 8.6</link> and mitigates session fixation.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_passing_a_sessionhandler_object_to_session_set_save_handler_which_does_not_contain_the_create_sid_and_validateid
-->
+ </simpara>
+
+ <simpara>
+ Accordingly, a deprecation notice is now emitted when a class implements
+ <interfacename>SessionHandlerInterface</interfacename> without defining
+ the <methodname>SessionIdInterface::create_sid</methodname> or
+ <methodname>SessionUpdateTimestampHandlerInterface::validateId</methodname>
+ methods.
+ In PHP 9.0 these methods will be moved from
+ <interfacename>SessionIdInterface</interfacename> and
+ <interfacename>SessionUpdateTimestampHandlerInterface</interfacename> into
+ <interfacename>SessionHandlerInterface</interfacename>, where they will be
+ required.
+ Implement both methods on custom save handlers now.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.spl">
+ <title>SPL</title>
+
+ <simpara>
+ The <function>spl_classes</function> function is now deprecated.
+ No other extension exposes such a function, and the Reflection extension
+ is always available.
+ Use <methodname>ReflectionExtension::getClassNames</methodname> instead.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_spl_classes
-->
+ </simpara>
+
+ <simpara>
+ The <function>spl_object_hash</function> function is now deprecated.
+ Since PHP 8.1 its return value has been nothing more than a reformatted
+ <function>spl_object_id</function>, and the name is misleading as the
+ value is not derived from the contents of the object.
+ The string form is also error-prone: two hashes can compare loosely equal
+ because both are numeric strings.
+ Use <function>spl_object_id</function> instead.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_spl_object_hash
-->
+ </simpara>
+
+ <para>
+ The following <classname>ArrayIterator</classname> methods are now
+ deprecated.
+ They only exist because <classname>ArrayIterator</classname> historically
+ shared its implementation with <classname>ArrayObject</classname>; most
+ of them make little sense on an iterator and prevent optimizing its
+ implementation.
+ The corresponding <classname>ArrayObject</classname> methods are not
+ deprecated.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_arrayiterator_methods_that_inherit_arrayobject_implementation
-->
+
+ <simplelist>
+ <member><methodname>ArrayIterator::getFlags</methodname></member>
+ <member><methodname>ArrayIterator::setFlags</methodname></member>
+ <member><methodname>ArrayIterator::asort</methodname></member>
+ <member><methodname>ArrayIterator::ksort</methodname></member>
+ <member><methodname>ArrayIterator::uasort</methodname></member>
+ <member><methodname>ArrayIterator::uksort</methodname></member>
+ <member><methodname>ArrayIterator::natsort</methodname></member>
+ <member><methodname>ArrayIterator::natcasesort</methodname></member>
+ <member><methodname>ArrayIterator::unserialize</methodname></member>
+ <member><methodname>ArrayIterator::serialize</methodname></member>
+ </simplelist>
+ </para>
+
+ <para>
+ The CSV methods of <classname>SplFileObject</classname> are now
+ deprecated.
+ Their historical design inconsistencies, further exposed by named
+ arguments, have made them increasingly difficult to maintain, and CSV
+ handling does not naturally belong in <classname>SplFileObject</classname>.
+ Use <function>fgetcsv</function> and <function>fputcsv</function> on a
+ stream instead.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#deprecate_splfileobject_csv_methods
-->
+
+ <simplelist>
+ <member><methodname>SplFileObject::fgetcsv</methodname></member>
+ <member><methodname>SplFileObject::fputcsv</methodname></member>
+ <member><methodname>SplFileObject::setCsvControl</methodname></member>
+ <member><methodname>SplFileObject::getCsvControl</methodname></member>
+ </simplelist>
+ </para>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.deprecated.zlib">
+ <title>Zlib</title>
+
+ <simpara>
+ Passing an object as the parameters of the
+ <link linkend="filters.compression">zlib.deflate and zlib.inflate</link>
+ stream filters is now deprecated, as only an associative array is
+ documented and supported.
+ Call <function>get_object_vars</function> on the object first and pass
+ the resulting array.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_as_parameters_to_the_zlibinflate_and_zlibdeflate_stream_filters
-->
+ </simpara>
+
+ <simpara>
+ Passing an object as the <parameter>options</parameter> argument to
+ <function>deflate_init</function> or <function>inflate_init</function>
+ is now deprecated, as objects are silently interpreted as the array of
+ their visible properties.
+ Call <function>get_object_vars</function> on the object first and pass
+ the resulting array.
+ <!-- RFC: https://wiki.php.net/rfc/deprecations_php_8_6#passing_objects_for_options_parameter_of_deflate_init_and_inflate_init
-->
+ </simpara>
+
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/incompatible.xml b/appendices/migration86/incompatible.xml
new file mode 100644
index 000000000000..bb64e35ce4cf
--- /dev/null
+++ b/appendices/migration86/incompatible.xml
@@ -0,0 +1,893 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.incompatible" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>Backward Incompatible Changes</title>
+
+ <sect2 xml:id="migration86.incompatible.core">
+ <title>PHP Core</title>
+
+ <sect3 xml:id="migration86.incompatible.core.foreach-by-reference">
+ <title>By-reference foreach after array compaction</title>
+
+ <simpara>
+ By-reference <link linkend="control-structures.foreach">foreach</link>
+ loops may now visit previously
+ skipped elements after array compaction.
+ Internal pointers on deleted elements now move to the next surviving
+ element during copy-on-write.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.magic-property-isset">
+ <title>Null coalescing and <function>empty</function> on magic
properties</title>
+
+ <simpara>
+ <literal>??</literal> and <function>empty</function> on a magic
property
+ no longer call <link linkend="object.get">__get()</link> when
+ <link linkend="object.isset">__isset()</link> has materialized the
+ property by writing into the property table.
+ The freshly-written value is returned directly.
+ <function>isset</function> applied directly to the property is unaffected;
+ nested forms such as <literal>isset($o->p['k'])</literal> follow
the same
+ path as <literal>??</literal>.
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+#[\AllowDynamicProperties]
+class Example
+{
+ public function __isset(string $name): bool
+ {
+ $this->$name = 'from __isset()';
+ return true;
+ }
+
+ public function __get(string $name): string
+ {
+ return 'from __get()';
+ }
+}
+
+$example = new Example();
+var_dump($example->foo ?? 'default');
+// As of PHP 8.6.0: string(14) "from __isset()"
+// Prior to PHP 8.6.0: string(12) "from __get()"
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.array-intersect">
+ <title>String conversion in <function>array_intersect</function></title>
+
+ <simpara>
+ <function>array_intersect</function> with at least two arrays now converts
+ values to strings while scanning its inputs instead of during sort
+ comparisons.
+ This can change the number and order of conversion warnings and
+ <link linkend="object.tostring">__toString()</link> calls, which
conversion
+ exception is reached, and the result for stateful
+ <link linkend="object.tostring">__toString()</link> implementations.
+ Argument types are validated before checking for empty arrays or
+ converting values, so an invalid later argument can suppress conversion
+ side effects from earlier arrays.
+ Values are not converted if any input array is empty.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.trim-form-feed">
+ <title>Form feed trimmed by default</title>
+
+ <simpara>
+ Form feed (<literal>\f</literal>) is now added to the default trimmed
+ characters of <function>trim</function>, <function>rtrim</function> and
+ <function>ltrim</function>.
+ <!-- RFC: https://wiki.php.net/rfc/trim_form_feed -->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+var_dump(trim("\f hello \f"));
+// As of PHP 8.6.0: string(5) "hello"
+// Prior to PHP 8.6.0: string(9) "\f hello \f"
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.new-value-errors">
+ <title>New errors for invalid arguments</title>
+
+ <para>
+ The following functions now raise a
+ <exceptionname>ValueError</exceptionname> when an invalid flag or mode
+ value is passed:
+
+ <simplelist>
+ <member><function>array_filter</function>
(<parameter>mode</parameter>)</member>
+ <member><function>array_change_key_case</function>
(<parameter>case</parameter>)</member>
+ <member><function>pathinfo</function>
(<parameter>flags</parameter>)</member>
+ <member><function>scandir</function>
(<parameter>sorting_order</parameter>)</member>
+ </simplelist>
+ </para>
+
+ <para>
+ The following functions now raise a
+ <exceptionname>ValueError</exceptionname> when the given argument contains
+ <literal>NUL</literal> bytes:
+
+ <simplelist>
+ <member><function>getenv</function> and
<function>putenv</function> (first argument)</member>
+ <member><function>dl</function>
(<parameter>extension_filename</parameter>)</member>
+ <member><function>openlog</function>
(<parameter>prefix</parameter>)</member>
+ <member><function>parse_str</function>
(<parameter>string</parameter>)</member>
+ <member><function>proc_open</function>
(<parameter>cwd</parameter>)</member>
+ </simplelist>
+ </para>
+
+ <para>
+ The following functions now raise a
+ <exceptionname>ValueError</exceptionname> for out-of-range values instead
+ of silently clamping them or allowing them to overflow:
+
+ <simplelist>
+ <member>
+ <function>number_format</function>, when
<parameter>decimals</parameter>
+ is outside the integer range.
+ </member>
+ <member>
+ <function>sleep</function>, when <parameter>seconds</parameter> is
+ greater than <literal>UINT_MAX</literal>, or
+ <literal>UINT_MAX / 1000</literal> on Windows.
+ </member>
+ <member>
+ <function>usleep</function>, when <parameter>microseconds</parameter>
is
+ greater than <literal>UINT_MAX</literal>.
+ </member>
+ </simplelist>
+ </para>
+
+ <simpara>
+ <function>linkinfo</function> now raises a
+ <exceptionname>ValueError</exceptionname> when the
+ <parameter>path</parameter> argument is empty.
+ </simpara>
+
+ <simpara>
+ <function>setlocale</function> now raises a
+ <exceptionname>ValueError</exceptionname> when a locale name contains
+ <literal>NUL</literal> bytes, instead of silently truncating it.
+ Arrays are now accepted only for the <parameter>locales</parameter>
+ argument.
+ Passing an array as a later variadic locale argument now throws a
+ <exceptionname>TypeError</exceptionname>.
+ Passing any additional locale arguments when
+ <parameter>locales</parameter> is an array now throws an
+ <exceptionname>ArgumentCountError</exceptionname>.
+ </simpara>
+
+ <para>
+ The following functions now raise a
+ <exceptionname>ValueError</exceptionname> when the
+ <parameter>filename</parameter> argument contains
<literal>NUL</literal>
+ bytes:
+
+ <simplelist>
+ <member><function>fileperms</function></member>
+ <member><function>fileinode</function></member>
+ <member><function>filesize</function></member>
+ <member><function>fileowner</function></member>
+ <member><function>filegroup</function></member>
+ <member><function>fileatime</function></member>
+ <member><function>filemtime</function></member>
+ <member><function>filectime</function></member>
+ <member><function>filetype</function></member>
+ <member><function>is_writable</function></member>
+ <member><function>is_readable</function></member>
+ <member><function>is_executable</function></member>
+ <member><function>is_file</function></member>
+ <member><function>is_dir</function></member>
+ <member><function>is_link</function></member>
+ <member><function>file_exists</function></member>
+ <member><function>lstat</function></member>
+ <member><function>stat</function></member>
+ </simplelist>
+ </para>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.imprecise-number-conversion">
+ <title>Notice on imprecise number conversion</title>
+
+ <simpara>
+ <function>base_convert</function>, <function>bindec</function>,
+ <function>hexdec</function> and <function>octdec</function> now raise a
+ notice when they cannot precisely convert the given number.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.incompatible.core.unpack-endianness">
+ <title>Endianness modifiers in <function>unpack</function> format
names</title>
+
+ <simpara>
+ <function>unpack</function> now reads a <literal><</literal> or
+ <literal>></literal> immediately following a format code as an
+ endianness modifier rather than as the first character of the element
+ name.
+ Formats such as <literal>"s<value"</literal> now produce the key
+ <literal>"value"</literal> instead of
<literal>"<value"</literal>, and
+ <literal>"C>name"</literal> raises a
+ <exceptionname>ValueError</exceptionname> because the
<literal>C</literal>
+ format code accepts no endianness modifier.
+ A name starting with these characters is unaffected when a repeater
+ precedes it, as in <literal>"s1<value"</literal>.
+ See also the
+ <link linkend="migration86.new-features.core.pack-unpack-endianness">new
+ endianness modifiers</link>.
+ <!-- RFC: https://wiki.php.net/rfc/pack-unpack-endianness-signed-integers-support
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+var_dump(unpack('s<value', "\x01\x00"));
+// As of PHP 8.6.0: ["value" => 1]
+// Prior to PHP 8.6.0: ["<value" => 1]
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.com">
+ <title>COM</title>
+
+ <simpara>
+ It is no longer possible to clone <classname>variant</classname> objects,
+ because the cloning behavior was ill-defined.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.curl">
+ <title>cURL</title>
+
+ <simpara>
+ The callback registered with <constant>CURLOPT_READFUNCTION</constant> now
+ throws a <exceptionname>ValueError</exceptionname> when returning an
+ integer other than <literal>0</literal>,
+ <constant>CURL_READFUNC_ABORT</constant> or
+ <constant>CURL_READFUNC_PAUSE</constant>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.dom">
+ <title>DOM</title>
+
+ <simpara>
+ Properties previously documented as read-only (e.g.
+ <property>DOMNode::$nodeType</property>,
+ <property>DOMDocument::$xmlEncoding</property>,
+ <property>DOMEntity::$actualEncoding</property>,
+ <property>DOMEntity::$encoding</property>,
+ <property>DOMEntity::$version</property>) are now declared with
+ <link linkend="language.oop5.visibility-members-aviz">asymmetric
+ visibility</link> (<code>public private(set)</code>).
+ Attempts to write to them from outside the class now raise
+ <quote>Cannot modify private(set) property
<replaceable>class</replaceable>::$<replaceable>prop</replaceable> from
global scope</quote>
+ instead of the prior readonly modification error.
+ <methodname>ReflectionProperty::isWritable</methodname> also reports these
+ properties accurately.
+ </simpara>
+
+ <simpara>
+ Array access on <classname>Dom\DtdNamedNodeMap</classname> objects now
+ returns &null; for negative integer indexes instead of returning the first
+ node, and raises a <exceptionname>ValueError</exceptionname> when the
+ integer index is greater than <constant>PHP_INT_MAX</constant> instead of
+ overflowing to a smaller index.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.ftp">
+ <title>FTP</title>
+
+ <simpara>
+ <function>ftp_nb_fget</function>, <function>ftp_nb_fput</function>,
+ <function>ftp_nb_get</function> and <function>ftp_nb_put</function> now
+ throw an <exceptionname>Error</exceptionname> when the connection is
+ already transferring, instead of emitting a warning and returning &false;.
+ <function>ftp_close</function> already throws on the same condition.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.gd">
+ <title>GD</title>
+
+ <simpara>
+ <function>imagesetstyle</function>, <function>imagefilter</function> and
+ <function>imagecrop</function> filter the types and values of their array
+ arguments and raise a <exceptionname>TypeError</exceptionname> or
+ <exceptionname>ValueError</exceptionname> accordingly.
+ </simpara>
+
+ <simpara>
+ <function>imageaffinematrixget</function> now enforces the documented
+ <type
class="union"><type>array</type><type>float</type></type>
type for
+ the <parameter>options</parameter> parameter, including the corresponding
+ weak and strict typing behavior.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.gmp">
+ <title>GMP</title>
+
+ <simpara>
+ <function>gmp_fact</function> now throws a
+ <exceptionname>ValueError</exceptionname> if <parameter>num</parameter>
+ does not fit into an unsigned long.
+ </simpara>
+
+ <simpara>
+ <function>gmp_pow</function>, <function>gmp_binomial</function>,
+ <function>gmp_root</function> and <function>gmp_rootrem</function> now
+ throw a <exceptionname>ValueError</exceptionname> if their second argument
+ does not fit into an unsigned long.
+ </simpara>
+
+ <simpara>
+ The shift (<literal><<</literal>,
<literal>>></literal>) and
+ exponentiation (<literal>**</literal>) operators on
+ <classname>GMP</classname> objects now throw a
+ <exceptionname>ValueError</exceptionname> when
<classname>GMP</classname>
+ right operands are outside the unsigned long range, instead of silently
+ truncating them.
+ </simpara>
+
+ <simpara>
+ <classname>GMP</classname> integer string parsing now throws a
+ <exceptionname>ValueError</exceptionname> for strings containing
+ <literal>NUL</literal> bytes, instead of silently truncating them.
+ </simpara>
+
+ <simpara>
+ The message of the <exceptionname>DivisionByZeroError</exceptionname>
+ raised by <function>gmp_powm</function> on modulo by zero now includes the
+ function name and argument index (<parameter>modulus</parameter>).
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.intl">
+ <title>Intl</title>
+
+ <simpara>
+ Passing a non-stringable object as a time zone to Intl
+ <acronym>API</acronym>s that accept time zone objects or strings now
+ raises a <exceptionname>TypeError</exceptionname> instead of an
+ <exceptionname>Error</exceptionname>.
+ </simpara>
+
+ <simpara>
+ <methodname>IntlIterator::current</methodname> now returns &null; when
+ called before the iterator is positioned, or after the iterator becomes
+ invalid, instead of exposing an undefined value.
+ </simpara>
+
+ <simpara>
+ <methodname>IntlBreakIterator::getLocale</methodname> now raises a
+ <exceptionname>ValueError</exceptionname> when the type is neither
+ <constant>Locale::ACTUAL_LOCALE</constant> nor
+ <constant>Locale::VALID_LOCALE</constant> instead of returning &false;.
+ </simpara>
+
+ <simpara>
+ <methodname>MessageFormatter::parse</methodname> and
+ <methodname>MessageFormatter::parseMessage</methodname> now return
+ <constant>PHP_INT_MIN</constant> as <type>int</type>, rather than
+ <type>float</type>, on 64-bit platforms when parsing integer values.
+ </simpara>
+
+ <simpara>
+ The <parameter>type</parameter> parameter of
+ <methodname>IntlBreakIterator::getPartsIterator</methodname> has been
+ changed from <type>string</type> to <type>int</type> to match the
+ underlying implementation.
+ </simpara>
+
+ <simpara>
+ <methodname>UConverter::transcode</methodname> now rejects
+ <literal>from_subst</literal> and <literal>to_subst</literal> option
values
+ longer than 127 bytes instead of silently truncating the length before
+ passing it to <acronym>ICU</acronym>.
+ </simpara>
+
+ <simpara>
+ <methodname>ResourceBundle::get</methodname> and
+ <function>resourcebundle_get</function> now report fallback-disabled
+ resource lookups with
+ <quote>without fallback to <replaceable>locale</replaceable></quote>
+ instead of the malformed
+ <quote>without fallback from to
<replaceable>locale</replaceable></quote>.
+ </simpara>
+
+ <simpara>
+
<methodname>IntlDateFormatter::parse</methodname>/<function>datefmt_parse</function>
+ and
+
<methodname>IntlDateFormatter::localtime</methodname>/<function>datefmt_localtime</function>
+ now raise a <exceptionname>TypeError</exceptionname> when the
+ <parameter>offset</parameter> argument is not of type <type>int</type>
+ instead of silently converting the value.
+ </simpara>
+
+ <simpara>
+ <methodname>Collator::sort</methodname>,
+ <function>collator_sort</function>,
+ <methodname>Collator::asort</methodname>, and
+ <function>collator_asort</function> now report
<acronym>UTF-8</acronym>/<acronym>UTF-16</acronym> conversion
+ failures during comparison through the intl error mechanism and return
+ &false;.
+ With <link linkend="ini.intl.use-exceptions">intl.use_exceptions</link>
+ enabled, these failures throw an <exceptionname>IntlException</exceptionname>.
+ Previously, these paths emitted a warning and compared the value as an
+ empty string.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.mbstring">
+ <title>MBString</title>
+
+ <simpara>
+ Unicode data tables have been updated to Unicode 18.0.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.pcntl">
+ <title>PCNTL</title>
+
+ <simpara>
+ <function>pcntl_alarm</function> now raises a
+ <exceptionname>ValueError</exceptionname> if the
+ <parameter>seconds</parameter> argument is lower than zero or greater
+ than the platform's <literal>UINT_MAX</literal>.
+ </simpara>
+
+ <simpara>
+ <function>pcntl_exec</function> now raises a
+ <exceptionname>ValueError</exceptionname> if the
+ <parameter>args</parameter> argument is not a list array.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.pcre">
+ <title>PCRE</title>
+
+ <simpara>
+ <function>preg_grep</function> now returns &false; instead of a partial
+ array when a <acronym>PCRE</acronym> execution error occurs (e.g. malformed
+ <acronym>UTF-8</acronym> input with
+ the <literal>u</literal> modifier).
+ This is consistent with the other <literal>preg_*</literal> functions.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.pgsql">
+ <title>PGSQL</title>
+
+ <simpara>
+ When <function>pg_fetch_object</function> is given a non-empty
+ <parameter>constructor_args</parameter> for a class without a constructor,
+ the <exceptionname>ValueError</exceptionname> now refers to the
+ <parameter>constructor_args</parameter> argument instead of
+ <parameter>class</parameter>.
+ Errors raised when the requested class is not instantiable (abstract,
+ interface, enum) now surface before the row is fetched.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.phar">
+ <title>Phar</title>
+
+ <simpara>
+ <methodname>Phar::mungServer</methodname> now raises a
+ <exceptionname>ValueError</exceptionname> when an invalid argument value
+ is passed instead of being silently ignored.
+ </simpara>
+
+ <simpara>
+ <methodname>Phar::addEmptyDir</methodname> now rejects
+ <literal>"/.phar"</literal> paths in addition to
<literal>".phar"</literal>
+ paths, and raises the same
+ <exceptionname>BadMethodCallException</exceptionname> for attempts to
+ create the reserved magic <literal>".phar"</literal> directory through
+ that form.
+ Non-magic names that merely share the <literal>".phar"</literal> prefix
+ are now treated as ordinary directories.
+ </simpara>
+
+ <simpara>
+ Files are only automatically interpreted as Phar archives when included if
+ <literal>".phar"</literal> occurs as an extension in the filename
component
+ of their paths.
+ Previously, it could occur in a directory name or as part of an extension
+ such as <literal>".pharma"</literal>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.session">
+ <title>Session</title>
+
+ <simpara>
+ Setting <link
linkend="ini.session.cookie-path">session.cookie_path</link>,
+ <link linkend="ini.session.cookie-domain">session.cookie_domain</link>, or
+ <link linkend="ini.session.cache-limiter">session.cache_limiter</link> to
a
+ value containing <literal>NUL</literal> bytes now emits a warning and
+ leaves the setting unchanged.
+ Previously, <literal>NUL</literal> bytes were silently accepted: for
+ <literal>cookie_path</literal> and <literal>cookie_domain</literal> this
+ caused the <acronym>SAPI</acronym> to drop the
+ <literal>Set-Cookie</literal> header; for
<literal>cache_limiter</literal>
+ the value was silently truncated at the <literal>NUL</literal> byte.
+ </simpara>
+
+ <simpara>
+ <function>session_module_name</function> now throws a
+ <exceptionname>ValueError</exceptionname> if <parameter>name</parameter>
+ is a string containing <literal>NUL</literal> bytes.
+ </simpara>
+
+ <simpara>
+ <function>session_encode</function> now returns an empty string instead of
+ &false; for empty sessions.
+ It only returns &false; now when the session data could not be encoded.
+ This mainly happens with the default serialization handler if a key
+ contains the pipe (<literal>|</literal>) character.
+ </simpara>
+
+ <simpara>
+ When <link linkend="ini.session.lazy-write">session.lazy_write</link> is
+ enabled and a session handler implements
+ <interfacename>SessionUpdateTimestampHandlerInterface</interfacename>,
+ sessions that were read as empty and remain empty at write time will now
+ trigger
<methodname>SessionUpdateTimestampHandlerInterface::updateTimestamp</methodname>
+ instead of <methodname>SessionHandlerInterface::write</methodname>.
+ Previously, <methodname>SessionHandlerInterface::write</methodname> was
+ always called for empty sessions because
+ <function>session_encode</function> returned &false;, bypassing the
+ lazy_write comparison.
+ Custom session handlers that rely on
+ <methodname>SessionHandlerInterface::write</methodname> being called with
+ empty data (e.g. to destroy the session) should implement the same logic
+ in their
<methodname>SessionUpdateTimestampHandlerInterface::updateTimestamp</methodname>
+ method.
+ </simpara>
+
+ <para>
+ The defaults of three session INI settings have changed to provide secure
+ behavior out of the box:
+ <!-- RFC: https://wiki.php.net/rfc/session_security_defaults
-->
+
+ <simplelist>
+ <member>
+ <link
linkend="ini.session.use-strict-mode">session.use_strict_mode</link>
+ is now <literal>1</literal> (was <literal>0</literal>).
+ Strict mode rejects uninitialized session IDs, mitigating session
+ fixation.
+ Custom session handlers that previously relied on accepting externally
+ supplied IDs without a corresponding storage entry must either implement
+ <methodname>SessionUpdateTimestampHandlerInterface::validateId</methodname>
+ and <methodname>SessionIdInterface::create_sid</methodname>, or
+ explicitly set this to <literal>0</literal>.
+ </member>
+ <member>
+ <link
linkend="ini.session.cookie-httponly">session.cookie_httponly</link>
+ is now <literal>1</literal> (was <literal>0</literal>).
+ Session cookies are no longer accessible to JavaScript via
+ <literal>document.cookie</literal>.
+ Applications that read the session cookie from JavaScript must
+ explicitly set this to <literal>0</literal>.
+ </member>
+ <member>
+ <link
linkend="ini.session.cookie-samesite">session.cookie_samesite</link>
+ is now <literal>"Lax"</literal> (was unset).
+ Session cookies are no longer sent on cross-site requests other than
+ top-level navigations using safe <acronym>HTTP</acronym> methods.
+ Applications that depend on session cookies being sent on cross-site
+ POST submissions must explicitly set this to <literal>"None"</literal>
+ (and also set
+ <link linkend="ini.session.cookie-secure">session.cookie_secure</link>
+ to <literal>1</literal>).
+ </member>
+ </simplelist>
+ </para>
+
+ <simpara>
+ <methodname>SessionHandler::validateId</methodname> has been added and
+ delegates to the configured save handler.
+ A subclass that declares <methodname>SessionHandler::validateId</methodname>
+ without a return type now emits a deprecation notice for the tentative
+ <type>bool</type> return type.
+ A subclass that overrides <methodname>SessionHandler::open</methodname>
+ without calling <code>parent::open()</code> keeps its previous behavior
+ and emits a warning when an ID is validated.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.shmop">
+ <title>Shmop</title>
+
+ <simpara>
+ <function>shmop_open</function> now raises a
+ <exceptionname>ValueError</exceptionname> when the
+ <parameter>key</parameter> argument is outside the platform's
+ <literal>key_t</literal> range instead of passing a truncated key to the
+ operating system.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.simplexml">
+ <title>SimpleXML</title>
+
+ <simpara>
+ <methodname>SimpleXMLElement::__construct</methodname> now raises a
+ <exceptionname>ValueError</exceptionname> when the
+ <parameter>data</parameter> argument contains <literal>NUL</literal>
+ bytes, matching <function>simplexml_load_file</function>.
+ With <parameter>dataIsURL</parameter> set, it previously truncated the
+ path at the first <literal>NUL</literal> byte.
+ Without it, the string went to libxml, which with default options rejects
+ a <literal>NUL</literal> on current versions but accepts the truncated
+ document on older ones and under <constant>LIBXML_RECOVER</constant>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.soap">
+ <title>SOAP</title>
+
+ <simpara>
+ The <literal>"classmap"</literal> option of
+ <classname>SoapClient</classname> and <classname>SoapServer</classname>
+ now rejects arrays containing integer keys.
+ Previously, sparse integer-keyed and mixed-keyed arrays could be accepted.
+ <classname>SoapClient</classname> now throws a
+ <exceptionname>TypeError</exceptionname> for non-array
+ <literal>"classmap"</literal> options and a
+ <exceptionname>ValueError</exceptionname> for arrays containing integer
+ keys, also when the <literal>"exceptions"</literal> option is disabled.
+ </simpara>
+
+ <simpara>
+ <acronym>WSDL</acronym>/<acronym>XML</acronym> Schema parsing now
rejects out-of-range integer values for
+ occurrence constraints and integer restriction facets.
+ Negative <literal>minOccurs</literal> and <literal>maxOccurs</literal>
+ values are rejected as well.
+ </simpara>
+
+ <simpara>
+ SOAP encoding errors now report the affected type or failing operation in
+ the error message instead of the generic
+ <quote>Encoding: Violation of encoding rules</quote> message.
+ Code that compares the exact message may need to be updated.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.sockets">
+ <title>Sockets</title>
+
+ <simpara>
+ <function>socket_set_option</function> with
+ <constant>SO_ATTACH_REUSEPORT_CBPF</constant> now requires an
+ <type>int</type> <parameter>value</parameter> and a
+ <parameter>level</parameter> of <constant>SOL_SOCKET</constant>.
+ Any other value type throws a <exceptionname>TypeError</exceptionname>
+ instead of being coerced, and any other level raises a warning and
+ returns &false;.
+ </simpara>
+
+ <simpara>
+ <function>socket_set_option</function> with
+ <constant>SO_ATTACH_REUSEPORT_CBPF</constant> and a
+ <parameter>value</parameter> of <literal>0</literal> now detaches the
+ reuseport filter through <constant>SO_DETACH_REUSEPORT_BPF</constant>.
+ It previously used <constant>SO_DETACH_BPF</constant>, an alias of
+ <constant>SO_DETACH_FILTER</constant>, which left the reuseport program
+ attached.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.sodium">
+ <title>Sodium</title>
+
+ <simpara>
+ The password-hashing functions <function>sodium_crypto_pwhash</function>,
+ <function>sodium_crypto_pwhash_str</function>,
+ <function>sodium_crypto_pwhash_scryptsalsa208sha256</function> and
+ <function>sodium_crypto_pwhash_scryptsalsa208sha256_str</function> now
+ throw a <exceptionname>ValueError</exceptionname> instead of a
+ <exceptionname>SodiumException</exceptionname> when an argument is out of
+ range, such as an <parameter>opslimit</parameter> or
+ <parameter>memlimit</parameter> below the documented minimum.
+ <exceptionname>SodiumException</exceptionname> is still thrown for
+ internal libsodium failures.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.spl">
+ <title>SPL</title>
+
+ <simpara>
+ <methodname>SplObjectStorage::getHash</methodname> implementations may no
+ longer mutate any <classname>SplObjectStorage</classname> instance.
+ Attempting to do so now throws an <exceptionname>Error</exceptionname>.
+ </simpara>
+
+ <simpara>
+ <methodname>SplFileObject::next</methodname> now advances the stream when
+ no prior <methodname>SplFileObject::current</methodname> call has cached a
+ line.
+ A subsequent <methodname>SplFileObject::current</methodname> call returns
+ the new line rather than the previous one.
+ </simpara>
+
+ <simpara>
+ <methodname>SplFileObject::fgets</methodname> no longer caches the returned
+ line for subsequent <methodname>SplFileObject::current</methodname> calls.
+ <methodname>SplFileObject::current</methodname> now re-reads from the
+ current stream position instead of returning the line
+ <methodname>SplFileObject::fgets</methodname> just returned.
+ </simpara>
+
+ <simpara>
+ <methodname>SplFileObject::next</methodname> past <acronym>EOF</acronym>
no longer increments
+ <methodname>SplFileObject::key</methodname> without bound.
+ <methodname>SplFileObject::seek</methodname> past <acronym>EOF</acronym>
now produces the same
+ <methodname>SplFileObject::key</methodname> value as
+ <classname>SplTempFileObject</classname>; the two previously returned
+ different values.
+ </simpara>
+
+ <simpara>
+ <methodname>DirectoryIterator::key</methodname> now returns
+ <type
class="union"><type>int</type><type>string</type></type>,
and
+ <methodname>DirectoryIterator::current</methodname> returns
+ <type
class="union"><type>string</type><type>SplFileInfo</type><type>static</type></type>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.sysvshm">
+ <title>Sysvshm</title>
+
+ <simpara>
+ <function>shm_attach</function> now raises a
+ <exceptionname>ValueError</exceptionname> when the
+ <parameter>key</parameter> argument is outside the platform's
+ <literal>key_t</literal> range instead of passing a truncated key to the
+ operating system.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.uri">
+ <title>URI</title>
+
+ <simpara>
+ <methodname>Uri\WhatWg\Url::__construct</methodname> now sets
+ <parameter>softErrors</parameter> to an empty array when URL processing
+ throws an exception, instead of preserving its value.
+ URL validation errors remain available in the
+ <property>Uri\WhatWg\InvalidUrlException::$errors</property> property.
+ </simpara>
+
+ <simpara>
+ <methodname>Uri\WhatWg\Url::__construct</methodname>,
+ <methodname>Uri\WhatWg\Url::parse</methodname>, the
+ <literal>Uri\WhatWg\Url::with*()</literal> methods, and
+ <methodname>Uri\WhatWg\UrlBuilder::build</methodname> now report
+ validation errors in the order in which they are detected.
+ This reverses the order of multiple errors compared to PHP 8.5.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.zip">
+ <title>Zip</title>
+
+ <simpara>
+ <methodname>ZipArchive::extractTo</methodname> now raises a
+ <exceptionname>TypeError</exceptionname> for the
+ <parameter>files</parameter> argument if one or more of the entries is not
+ a string.
+ </simpara>
+
+ <simpara>
+ <methodname>ZipArchive::addGlob</methodname> and
+ <methodname>ZipArchive::addPattern</methodname> now raise a
+ <exceptionname>TypeError</exceptionname> when the
+ <literal>"remove_all_path"</literal> option is not of type
+ <type>bool</type>, or when the
<literal>"comp_method"</literal>,
+ <literal>"comp_flags"</literal>, or
<literal>"enc_method"</literal>
+ options are not of type <type>int</type> (numeric strings are coerced),
+ instead of emitting a warning.
+ </simpara>
+
+ <simpara>
+ Serializing or unserializing a <classname>ZipArchive</classname> now
+ throws an <exceptionname>Exception</exceptionname>.
+ A subclass can override
+ <link linkend="object.serialize">__serialize()</link> and
+ <link linkend="object.unserialize">__unserialize()</link> to round-trip
through
+ <methodname>ZipArchive::closeString</methodname> and
+ <methodname>ZipArchive::openString</methodname>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.incompatible.zlib">
+ <title>Zlib</title>
+
+ <simpara>
+ <function>deflate_init</function> now raises a
+ <exceptionname>TypeError</exceptionname> when the value for option
+ <literal>"level"</literal>,
<literal>"memory"</literal>,
+ <literal>"window"</literal>, or
<literal>"strategy"</literal> is not of
+ type <type>int</type>.
+ </simpara>
+
+ <simpara>
+ <function>inflate_init</function> now raises a
+ <exceptionname>TypeError</exceptionname> when the value for option
+ <literal>"window"</literal> is not of type <type>int</type>.
+ </simpara>
+
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/new-classes.xml b/appendices/migration86/new-classes.xml
new file mode 100644
index 000000000000..6467a582d4b2
--- /dev/null
+++ b/appendices/migration86/new-classes.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.new-classes" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>New Classes and Interfaces</title>
+
+ <sect2 xml:id="migration86.new-classes.core">
+ <title>Core</title>
+ <simplelist>
+ <member><enumname>SortDirection</enumname></member>
+ <member><exceptionname>StreamError</exceptionname></member>
+ <member><exceptionname>StreamException</exceptionname></member>
+ <member><enumname>StreamErrorStore</enumname></member>
+ <member><enumname>StreamErrorMode</enumname></member>
+ <member><enumname>StreamErrorCode</enumname></member>
+ <member><exceptionname>Io\IoException</exceptionname></member>
+ <member><classname>Io\Poll\Context</classname></member>
+ <member><classname>Io\Poll\Watcher</classname></member>
+ <member><enumname>Io\Poll\Backend</enumname></member>
+ <member><enumname>Io\Poll\Event</enumname></member>
+ <member><interfacename>Io\Poll\Handle</interfacename></member>
+ <member><exceptionname>Io\Poll\PollException</exceptionname></member>
+
<member><exceptionname>Io\Poll\FailedPollOperationException</exceptionname></member>
+
<member><exceptionname>Io\Poll\FailedContextInitializationException</exceptionname></member>
+
<member><exceptionname>Io\Poll\FailedHandleAddException</exceptionname></member>
+
<member><exceptionname>Io\Poll\FailedWatcherModificationException</exceptionname></member>
+
<member><exceptionname>Io\Poll\FailedPollWaitException</exceptionname></member>
+
<member><exceptionname>Io\Poll\BackendUnavailableException</exceptionname></member>
+
<member><exceptionname>Io\Poll\InactiveWatcherException</exceptionname></member>
+
<member><exceptionname>Io\Poll\HandleAlreadyWatchedException</exceptionname></member>
+
<member><exceptionname>Io\Poll\InvalidHandleException</exceptionname></member>
+ <member><classname>StreamPollHandle</classname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/sort_direction_enum
-->
+ <!-- RFC: https://wiki.php.net/rfc/stream_errors -->
+ <!-- RFC: https://wiki.php.net/rfc/poll_api
-->
+ </sect2>
+
+ <sect2 xml:id="migration86.new-classes.date">
+ <title>Date</title>
+ <simplelist>
+ <member><classname>Time\Duration</classname></member>
+ <member><exceptionname>Time\TimeException</exceptionname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/duration_class -->
+ </sect2>
+
+ <sect2 xml:id="migration86.new-classes.intl">
+ <title>Intl</title>
+ <simplelist>
+ <member><classname>IntlNumberRangeFormatter</classname></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-classes.openssl">
+ <title>OpenSSL</title>
+ <simplelist>
+ <member><exceptionname>Openssl\OpensslException</exceptionname></member>
+ <member><classname>Openssl\Session</classname></member>
+ <member><classname>Openssl\Psk</classname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/tls_session_resumption
-->
+ </sect2>
+
+ <sect2 xml:id="migration86.new-classes.snmp">
+ <title>SNMP</title>
+ <simplelist>
+ <member><enumname>Snmp\Mib</enumname></member>
+ <member><enumname>Snmp\OidOutput</enumname></member>
+ <member><enumname>Snmp\Output</enumname></member>
+ <member><enumname>Snmp\StringOutput</enumname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/snmp_improvements_2026
-->
+ </sect2>
+
+ <sect2 xml:id="migration86.new-classes.uri">
+ <title>URI</title>
+ <simplelist>
+ <member><classname>Uri\Rfc3986\UriBuilder</classname></member>
+ <member><classname>Uri\WhatWg\UrlBuilder</classname></member>
+ <member><enumname>Uri\WhatWg\UrlPercentEncodingMode</enumname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#uri_building
-->
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#percent-encoding_support
-->
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/new-features.xml b/appendices/migration86/new-features.xml
new file mode 100644
index 000000000000..ab2bdb8034bd
--- /dev/null
+++ b/appendices/migration86/new-features.xml
@@ -0,0 +1,635 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.new-features" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <title>New Features</title>
+
+ <sect2 xml:id="migration86.new-features.core">
+ <title>PHP Core</title>
+
+ <sect3 xml:id="migration86.new-features.core.readonly-property-defaults">
+ <title>Default values for readonly properties</title>
+
+ <simpara>
+ <link linkend="language.oop5.properties.readonly-properties">Readonly
+ properties</link> may now declare default values.
+ <!-- RFC: https://wiki.php.net/rfc/readonly_property_defaults
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+class Config
+{
+ public readonly int $timeout = 30;
+}
+
+$config = new Config();
+var_dump($config->timeout); // int(30)
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.weakmap-reference-assignment">
+ <title>Reference assignment on WeakMap</title>
+
+ <simpara>
+ It is now possible to use reference assignment on a
+ <classname>WeakMap</classname> without the key needing to be present
+ beforehand.
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+$obj = new stdClass();
+$map = new WeakMap();
+$count = 0;
+
+// Previously: Error: "Object stdClass#1 not contained in WeakMap"
+$map[$obj] = &$count;
+$count++;
+
+var_dump($map[$obj]); // int(1)
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.debuginfo-on-enums">
+ <title><methodname>__debugInfo</methodname> on enums</title>
+
+ <simpara>
+ It is now possible to define the
+ <link linkend="language.oop5.magic.debuginfo">__debugInfo()</link>
+ magic method on <link linkend="language.enumerations">enums</link>.
+ Previously, this resulted in a fatal error.
+ <!-- RFC: https://wiki.php.net/rfc/debugable-enums -->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+enum Suit: string
+{
+ case Hearts = 'H';
+ case Spades = 'S';
+
+ public function __debugInfo(): array
+ {
+ return ['suit' => $this->name, 'symbol' => $this->value];
+ }
+}
+
+var_dump(Suit::Hearts);
+]]>
+ </programlisting>
+ &example.outputs;
+ <screen>
+<![CDATA[
+enum(Suit::Hearts) (2) {
+ ["suit"]=>
+ string(6) "Hearts"
+ ["symbol"]=>
+ string(1) "H"
+}
+]]>
+ </screen>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.override-for-constants">
+ <title><code>#[\Override]</code> for class constants</title>
+
+ <simpara>
+ The <classname>Override</classname> attribute can now be applied to
+ class constants, including enum cases.
+ As with methods and properties, a compile-time error is raised if no
+ matching constant exists in a parent class or implemented interface.
+ <!-- RFC: https://wiki.php.net/rfc/override_constants
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+interface HasVersion
+{
+ const VERSION = '1.0';
+}
+
+class Package implements HasVersion
+{
+ #[\Override]
+ const VERSION = '2.0';
+
+ // Fatal error: Package::NAME has #[\Override] attribute,
+ // but no matching parent constant exists
+ // #[\Override]
+ // const NAME = 'package';
+}
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.partial-function-application">
+ <title>Partial function application</title>
+
+ <simpara>
+ Added support for partial function application. A call in which one or
+ more arguments are replaced by the <literal>?</literal> placeholder, or
+ the remaining arguments by the <literal>...</literal> placeholder,
+ creates a <classname>Closure</classname> that accepts the missing
+ arguments.
+ <!-- RFC: https://wiki.php.net/rfc/partial_function_application_v2
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+function add(int $a, int $b, int $c = 0): int
+{
+ return $a + $b + $c;
+}
+
+// "?" replaces exactly one argument
+$addOne = add(1, ?);
+var_dump($addOne(5)); // int(6)
+
+// "..." replaces all remaining arguments
+$addToOne = add(1, ...);
+var_dump($addToOne(2, 3)); // int(6)
+
+// Works with any callable, including internal functions
+$stripSpaces = str_replace(' ', '', ?);
+var_dump($stripSpaces('a b c')); // string(3) "abc"
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.const-object-property-write">
+ <title>Mutating objects stored in constants</title>
+
+ <simpara>
+ Objects stored in constants or class constants can now be mutated
+ directly via <code>OBJ->prop = $val</code>.
+ <!-- RFC: https://wiki.php.net/rfc/const_object_property_write
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+const CONFIG = new stdClass();
+
+// Previously: "Fatal error: Cannot use temporary expression in write context"
+CONFIG->debug = true;
+
+var_dump(CONFIG->debug); // bool(true)
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.new-features.core.pack-unpack-endianness">
+ <title>Endianness modifiers for pack() and unpack()</title>
+
+ <simpara>
+ <function>pack</function> and <function>unpack</function> now accept
the
+ <literal><</literal> (little-endian) and
<literal>></literal>
+ (big-endian) modifiers after the <literal>s</literal>,
<literal>S</literal>,
+ <literal>l</literal>, <literal>L</literal>,
<literal>q</literal> and
+ <literal>Q</literal> integer format codes, and after the
+ <literal>f</literal> and <literal>d</literal> float and double format
codes.
+ <!-- RFC: https://wiki.php.net/rfc/pack-unpack-endianness-signed-integers-support
-->
+ <!-- RFC: https://wiki.php.net/rfc/pack-unpack-float-endianness-modifier
-->
+ </simpara>
+
+ <informalexample>
+ <programlisting role="php">
+<![CDATA[
+<?php
+echo bin2hex(pack('l>', 1)), PHP_EOL; // Prints "00000001"
+echo bin2hex(pack('l<', 1)), PHP_EOL; // Prints "01000000"
+echo bin2hex(pack('f>', 1.5)), PHP_EOL; // Prints "3fc00000"
+
+var_dump(unpack('s>value', "\xff\xfe")); // ["value" =>
int(-2)]
+]]>
+ </programlisting>
+ </informalexample>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.curl">
+ <title>cURL</title>
+
+ <simpara>
+ The array returned by <function>curl_getinfo</function> now includes a new
+ <literal>"size_delivered"</literal> key, which indicates the total number
of
+ bytes passed to the download write callback.
+ This value can also be obtained by passing
+ <constant>CURLINFO_SIZE_DELIVERED</constant> as the
+ <parameter>option</parameter> parameter.
+ Requires libcurl 8.20.0 or later.
+ </simpara>
+
+ <simpara>
+ Added <constant>CURLOPT_SEEKFUNCTION</constant> to register a callback that
+ repositions a streamed request body, so libcurl can rewind and resend it on
+ a redirect, multi-pass authentication, or a retried reused connection
+ instead of failing with <constant>CURLE_SEND_FAIL_REWIND</constant>.
+ The callback receives the <classname>CurlHandle</classname>, offset and
+ origin, and must return one of <constant>CURL_SEEKFUNC_OK</constant>,
+ <constant>CURL_SEEKFUNC_FAIL</constant> or
+ <constant>CURL_SEEKFUNC_CANTSEEK</constant>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.date">
+ <title>Date</title>
+
+ <simpara>
+ Added the <classname>Time\Duration</classname> class.
+ <!-- RFC: https://wiki.php.net/rfc/duration_class -->
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.fileinfo">
+ <title>FileInfo</title>
+
+ <simpara>
+ <function>finfo_file</function> now works with remote streams.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.gmp">
+ <title>GMP</title>
+
+ <simpara>
+ Added <function>gmp_powm_sec</function> for side-channel quiet modular
+ exponentiation.
+ Requires GNU MP 5.0.0 or later; it is not available on official Windows
+ builds using MPIR.
+ </simpara>
+
+ <simpara>
+ Added <function>gmp_prevprime</function> to get the largest prime smaller
+ than the given number.
+ The optional <parameter>definitely_prime</parameter> output parameter
+ indicates whether the returned number is definitely prime, as opposed to
+ probably prime.
+ A <exceptionname>ValueError</exceptionname> is thrown if no such prime
+ exists.
+ This function is only available when PHP is built against GNU MP 6.3.0 or
+ later; it is not available on official Windows builds using MPIR.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.intl">
+ <title>Intl</title>
+
+ <simpara>
+ Added the static methods
+ <methodname>IntlDatePatternGenerator::getSkeleton</methodname> and
+ <methodname>IntlDatePatternGenerator::getBaseSkeleton</methodname> to
+ generate the unique skeleton and base skeleton for a date/time pattern.
+ </simpara>
+
+ <simpara>
+ Added <methodname>Locale::getDisplayKeyword</methodname> and
+ <methodname>Locale::getDisplayKeywordValue</methodname>, with the aliases
+ <function>locale_get_display_keyword</function> and
+ <function>locale_get_display_keyword_value</function>, respectively.
+ <!-- RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
-->
+ </simpara>
+
+ <para>
+ Added the <classname>IntlNumberRangeFormatter</classname> class to format
+ an interval of two numbers with a given skeleton and locale.
+ It is supported as of <acronym>ICU</acronym> 63.
+ The collapse behavior is one of:
+ <simplelist>
+
<member><constant>IntlNumberRangeFormatter::COLLAPSE_AUTO</constant></member>
+
<member><constant>IntlNumberRangeFormatter::COLLAPSE_NONE</constant></member>
+
<member><constant>IntlNumberRangeFormatter::COLLAPSE_UNIT</constant></member>
+
<member><constant>IntlNumberRangeFormatter::COLLAPSE_ALL</constant></member>
+ </simplelist>
+ The identity fallback is one of:
+ <simplelist>
+
<member><constant>IntlNumberRangeFormatter::IDENTITY_FALLBACK_SINGLE_VALUE</constant></member>
+
<member><constant>IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE</constant></member>
+
<member><constant>IntlNumberRangeFormatter::IDENTITY_FALLBACK_APPROXIMATELY</constant></member>
+
<member><constant>IntlNumberRangeFormatter::IDENTITY_FALLBACK_RANGE</constant></member>
+ </simplelist>
+ </para>
+
+ <simpara>
+ Added <methodname>SpoofChecker::areBidiConfusable</methodname> to check
+ whether two strings are confusable for a given text direction, along with
+ the <constant>SpoofChecker::LTR</constant> and
+ <constant>SpoofChecker::RTL</constant> direction constants.
+ It is supported as of <acronym>ICU</acronym> 74.
+ </simpara>
+
+ <simpara>
+ Added <methodname>SpoofChecker::getBidiSkeleton</methodname> to generate a
+ confusable skeleton for a given text direction.
+ It is supported as of <acronym>ICU</acronym> 74.
+ </simpara>
+
+ <simpara>
+ Added <methodname>SpoofChecker::getSkeleton</methodname> to generate a
+ confusable skeleton for a given string.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.io">
+ <title>IO</title>
+
+ <para>
+ Added a new polling <acronym>API</acronym>, consisting of:
+ <simplelist>
+ <member>The <classname>Io\Poll\Context</classname> and
<classname>Io\Poll\Watcher</classname> classes</member>
+ <member>The <enumname>Io\Poll\Backend</enumname> and
<enumname>Io\Poll\Event</enumname> enums</member>
+ <member>The <interfacename>Io\Poll\Handle</interfacename>
interface</member>
+ <member>The <classname>StreamPollHandle</classname> class to watch
streams</member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/poll_api
-->
+ </para>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.json">
+ <title>JSON</title>
+
+ <simpara>
+ Added extra information about the error location to the error messages
+ returned by <function>json_last_error_msg</function> and to the message of
+ <exceptionname>JsonException</exceptionname>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.openssl">
+ <title>OpenSSL</title>
+
+ <para>
+ Added <acronym>TLS</acronym> session resumption support for streams.
+ This allows saving and restoring client sessions across requests,
+ implementing custom server-side session storage, and controlling session
+ cache behavior, using the following new
+ <link linkend="context.ssl">SSL context</link> options:
+ <simplelist>
+ <member><literal>session_data</literal></member>
+ <member><literal>session_new_cb</literal></member>
+ <member><literal>session_cache</literal></member>
+ <member><literal>session_cache_size</literal></member>
+ <member><literal>session_timeout</literal></member>
+ <member><literal>session_id_context</literal></member>
+ <member><literal>session_get_cb</literal></member>
+ <member><literal>session_remove_cb</literal></member>
+ <member><literal>num_tickets</literal></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/tls_session_resumption
-->
+ </para>
+
+ <simpara>
+ Added <acronym>TLS</acronym> external <acronym>PSK</acronym> support for
+ streams with the new <link linkend="context.ssl">SSL context</link>
+ options <literal>psk_client_cb</literal> and
+ <literal>psk_server_cb</literal>.
+ This allows setting and receiving pre-shared keys.
+ </simpara>
+
+ <simpara>
+ Added <acronym>TLS</acronym> 1.3 early data (0-RTT) support for streams.
+ Clients send early data with the <literal>early_data</literal>
+ <link linkend="context.ssl">SSL context</link> option; servers accept it
+ with <literal>max_early_data</literal> and receive it through the
+ <literal>early_data_cb</literal> callback.
+ The outcome is reported as <literal>"accepted"</literal>,
+ <literal>"rejected"</literal> or
<literal>"not_sent"</literal> in the
+ <literal>"early_data"</literal> key of the
<literal>"crypto"</literal>
+ entry of the <function>stream_get_meta_data</function> array.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.pdo-pgsql">
+ <title>PDO_PGSQL</title>
+
+ <simpara>
+ Added <constant>Pdo\Pgsql::ATTR_CHUNK_SIZE</constant>, the number of rows a
+ statement fetches per chunk.
+ A value of 1 or more enters the lazy fetch mode of
+ <code>PDO::ATTR_PREFETCH => 0</code>.
+ Setting <constant>PDO::ATTR_PREFETCH</constant> replaces the chunk size.
+ Statements that are prepared with neither it nor
+ <constant>PDO::ATTR_PREFETCH</constant> fall back to the value set on the
+ connection.
+ Requires libpq 17 or later.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.phar">
+ <title>Phar</title>
+
+ <simpara>
+ Overriding the <methodname>SplFileInfo::getMTime</methodname> and
+ <methodname>SplFileInfo::getPathname</methodname> methods now influences
+ the result of the <methodname>Phar::buildFromDirectory</methodname> and
+ <methodname>Phar::buildFromIterator</methodname> family of methods.
+ This makes it possible to override the timestamp and names of files.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.snmp">
+ <title>SNMP</title>
+
+ <simpara>
+ It is now possible to use <literal>AES192</literal>,
+ <literal>AES192C</literal>, <literal>AES256</literal>, and
+ <literal>AES256C</literal> as SNMPv3 security protocols if the underlying
+ library supports them.
+ <!-- RFC: https://wiki.php.net/rfc/snmp_improvements_2026#increase_the_number_of_snmpv3_security_protocols_supported
-->
+ </simpara>
+
+ <simpara>
+ It is now possible to reset the <acronym>MIB</acronym> tree using the new
+ <function>snmp_init_mib</function> function.
+ <!-- RFC: https://wiki.php.net/rfc/snmp_improvements_2026#allow_the_snmp_mib_to_be_reset
-->
+ </simpara>
+
+ <para>
+ Additional <acronym>MIB</acronym> parsing and output control functionality
+ has been exposed via:
+ <simplelist>
+ <member><function>snmp_set_mib_option</function></member>
+ <member><function>snmp_set_output_option</function></member>
+ <member><function>snmp_set_string_output_format</function></member>
+ <member><property>SNMP::$numeric_index</property></member>
+ <member><property>SNMP::$numeric_timeticks</property></member>
+ <member><property>SNMP::$extended_index</property></member>
+ <member><property>SNMP::$dont_print_units</property></member>
+ <member><property>SNMP::$escape_quotes</property></member>
+ <member><property>SNMP::$print_hex_text</property></member>
+ <member><methodname>SNMP::setOidOutputFormat</methodname></member>
+ <member><methodname>SNMP::setStringOutputFormat</methodname></member>
+ </simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/snmp_improvements_2026#implement_more_mib_parsing_and_value_output_controls
-->
+ </para>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.streams">
+ <title>Streams</title>
+
+ <para>
+ Added a new stream errors <acronym>API</acronym>, consisting of:
+ <simplelist>
+ <member>The <exceptionname>StreamError</exceptionname> and
<exceptionname>StreamException</exceptionname> classes</member>
+ <member>The <enumname>StreamErrorStore</enumname>,
<enumname>StreamErrorMode</enumname>, and
<enumname>StreamErrorCode</enumname> enums</member>
+ <member>The <function>stream_last_errors</function> and
<function>stream_clear_errors</function> functions</member>
+ </simplelist>
+ It is controlled using the <literal>error_mode</literal>,
+ <literal>error_store</literal> and <literal>error_handler</literal>
+ stream context options.
+ <!-- RFC: https://wiki.php.net/rfc/stream_errors -->
+ </para>
+
+ <simpara>
+ Added the <literal>filter.max_filter_count</literal> stream context option
+ for <link linkend="wrappers.php">php://filter</link> URLs.
+ When set, opening the stream fails with a warning if the URL would add more
+ filters than the configured value.
+ Negative values disable the check.
+ <!-- RFC: https://wiki.php.net/rfc/limit-maximum-number-of-filter-chains
-->
+ </simpara>
+
+ <simpara>
+ Added the <link linkend="context.socket">socket context</link> option
+ <literal>so_reuseaddr</literal> that allows disabling address reuse
+ (<literal>SO_REUSEADDR</literal>) and explicitly uses
+ <literal>SO_EXCLUSIVEADDRUSE</literal> on Windows.
+ </simpara>
+
+ <simpara>
+ Added the <link linkend="context.socket">socket context</link> options
+ <literal>so_keepalive</literal>, <literal>tcp_keepidle</literal>,
+ <literal>tcp_keepintvl</literal> and <literal>tcp_keepcnt</literal> that
+ allow setting socket keepalive options.
+ </simpara>
+
+ <simpara>
+ Added the <link linkend="context.socket">socket context</link> option
+ <literal>so_linger</literal> that sets <literal>SO_LINGER</literal> on
+ <acronym>TCP</acronym> sockets.
+ A positive value enables lingering for that many seconds, zero or a
+ negative value disables it.
+ Values above 65535 are clamped as the linger time is limited to an
+ unsigned short on some platforms.
+ </simpara>
+
+ <simpara>
+ Added the <link linkend="context.socket">socket context</link> options
+ <literal>so_rcvbuf</literal> and <literal>so_sndbuf</literal> that set
the
+ socket receive and send buffer sizes in bytes
+ (<literal>SO_RCVBUF</literal> and <literal>SO_SNDBUF</literal>) on
+ <acronym>TCP</acronym> and <acronym>UDP</acronym> sockets.
+ The value must be an integer between 1 and 2147483647, any other value
+ makes the stream creation fail.
+ The operating system may round, cap or otherwise adjust the requested size,
+ and may stop sizing that buffer automatically, so the size read back can
+ differ from the one requested.
+ </simpara>
+
+ <simpara>
+ Filtered streams can now be cast to file descriptors for use with
+ <function>stream_select</function>.
+ </simpara>
+
+ <para>
+ Added the <literal>"write_seek_mode"</literal> filter parameter for the
+ bz2, iconv, zlib, and <link linkend="filters.convert">convert</link>
stream
+ filters.
+ This parameter must be set via an associative array where the key is
+ <literal>"write_seek_mode"</literal> and the value is one of the following
+ strings:
+ <simplelist>
+ <member><literal>"preserve"</literal></member>
+ <member><literal>"reset"</literal></member>
+ <member><literal>"strict"</literal></member>
+ </simplelist>
+ </para>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.new-features.uri">
+ <title>URI</title>
+
+ <simpara>
+ Added <methodname>Uri\Rfc3986\Uri::getUriType</methodname> and
+ <methodname>Uri\WhatWg\Url::isSpecialScheme</methodname>.
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection
-->
+ </simpara>
+
+ <simpara>
+ Added <methodname>Uri\Rfc3986\Uri::getHostType</methodname> and
+ <methodname>Uri\WhatWg\Url::getHostType</methodname>.
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
-->
+ </simpara>
+
+ <simpara>
+ Added the <classname>Uri\Rfc3986\UriBuilder</classname> and
+ <classname>Uri\WhatWg\UrlBuilder</classname> classes.
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#uri_building
-->
+ </simpara>
+
+ <simpara>
+ Added <function>Uri\WhatWg\url_percent_encode</function>.
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#percent-encoding_support
-->
+ </simpara>
+
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/new-functions.xml b/appendices/migration86/new-functions.xml
new file mode 100644
index 000000000000..a20e69c629a1
--- /dev/null
+++ b/appendices/migration86/new-functions.xml
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.new-functions">
+ <title>New Functions</title>
+
+ <sect2 xml:id="migration86.new-functions.core">
+ <title>Core</title>
+
+ <simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/clamp_v2
-->
+ <member>
+ <function>clamp</function> returns the given value if in range, else
+ returns the nearest bound.
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/stream_errors -->
+ <member><function>stream_last_errors</function></member>
+ <member><function>stream_clear_errors</function></member>
+ <member><function>stream_socket_get_crypto_status</function></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.gmp">
+ <title>GMP</title>
+
+ <simplelist>
+ <member><function>gmp_powm_sec</function></member>
+ <member><function>gmp_prevprime</function></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.intl">
+ <title>Intl</title>
+
+ <simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/grapheme_strrev -->
+ <member><function>grapheme_strrev</function></member>
+ <member>
+ <methodname>IntlDatePatternGenerator::getSkeleton</methodname>
+ </member>
+ <member>
+ <methodname>IntlDatePatternGenerator::getBaseSkeleton</methodname>
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/getdisplaykeyword_and_getdisplaykeywordvalue
-->
+ <member>
+ <methodname>Locale::getDisplayKeyword</methodname>
+ </member>
+ <member><function>locale_get_display_keyword</function></member>
+ <member>
+ <methodname>Locale::getDisplayKeywordValue</methodname>
+ </member>
+ <member><function>locale_get_display_keyword_value</function></member>
+ <member>
+ <methodname>SpoofChecker::areBidiConfusable</methodname>
+ </member>
+ <member>
+ <methodname>SpoofChecker::getBidiSkeleton</methodname>
+ </member>
+ <member>
+ <methodname>SpoofChecker::getSkeleton</methodname>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.mysqli">
+ <title>MySQLi</title>
+
+ <simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/mysqli_quote_string
-->
+ <member>
+ <methodname>mysqli::quote_string</methodname>
+ </member>
+ <member><function>mysqli_quote_string</function></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.reflection">
+ <title>Reflection</title>
+
+ <simplelist>
+ <member>
+ <methodname>ReflectionAttribute::inNamespace</methodname>
+ </member>
+ <member>
+ <methodname>ReflectionAttribute::getNamespaceName</methodname>
+ </member>
+ <member>
+ <methodname>ReflectionAttribute::getShortName</methodname>
+ </member>
+ <member>
+ <methodname>ReflectionConstant::inNamespace</methodname>
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/parameter-doccomments
-->
+ <member>
+ <methodname>ReflectionParameter::getDocComment</methodname>
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/isreadable-iswriteable
-->
+ <member>
+ <methodname>ReflectionProperty::isReadable</methodname>
+ </member>
+ <member>
+ <methodname>ReflectionProperty::isWritable</methodname>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.snmp">
+ <title>SNMP</title>
+
+ <simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/snmp_improvements_2026
-->
+ <member><function>snmp_init_mib</function></member>
+ <member><function>snmp_set_mib_option</function></member>
+ <member><function>snmp_set_output_option</function></member>
+ <member><function>snmp_set_string_output_format</function></member>
+ <member>
+ <methodname>SNMP::setOidOutputFormat</methodname>
+ </member>
+ <member>
+ <methodname>SNMP::setStringOutputFormat</methodname>
+ </member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.sodium">
+ <title>Sodium</title>
+
+ <simplelist>
+ <member><function>sodium_crypto_ipcrypt_keygen</function></member>
+ <member><function>sodium_crypto_ipcrypt_encrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_decrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_nd_keygen</function></member>
+ <member><function>sodium_crypto_ipcrypt_nd_encrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_nd_decrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_ndx_keygen</function></member>
+ <member><function>sodium_crypto_ipcrypt_ndx_encrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_ndx_decrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_pfx_keygen</function></member>
+ <member><function>sodium_crypto_ipcrypt_pfx_encrypt</function></member>
+ <member><function>sodium_crypto_ipcrypt_pfx_decrypt</function></member>
+ <member><function>sodium_bin2ip</function></member>
+ <member><function>sodium_ip2bin</function></member>
+ </simplelist>
+
+ <para>
+ The following functions expose the X-Wing <acronym>KEM</acronym>
+ (hybrid ML-KEM768+X25519, libsodium's recommended <acronym>KEM</acronym>).
+ They are available when PHP is built against libsodium 1.0.22 or later.
+
+ <simplelist>
+ <member><function>sodium_crypto_kem_keypair</function></member>
+ <member><function>sodium_crypto_kem_seed_keypair</function></member>
+ <member><function>sodium_crypto_kem_secretkey</function></member>
+ <member><function>sodium_crypto_kem_publickey</function></member>
+ <member><function>sodium_crypto_kem_enc</function></member>
+ <member><function>sodium_crypto_kem_dec</function></member>
+ </simplelist>
+ </para>
+
+ <para>
+ The following functions expose the ML-KEM768 (FIPS 203)
+ <acronym>KEM</acronym>.
+ They are available when PHP is built against libsodium 1.0.22 or later.
+
+ <simplelist>
+
<member><function>sodium_crypto_kem_mlkem768_keypair</function></member>
+
<member><function>sodium_crypto_kem_mlkem768_seed_keypair</function></member>
+
<member><function>sodium_crypto_kem_mlkem768_secretkey</function></member>
+
<member><function>sodium_crypto_kem_mlkem768_publickey</function></member>
+ <member><function>sodium_crypto_kem_mlkem768_enc</function></member>
+ <member><function>sodium_crypto_kem_mlkem768_dec</function></member>
+ </simplelist>
+ </para>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.uri">
+ <title>URI</title>
+
+ <simplelist>
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection
-->
+ <member>
+ <methodname>Uri\Rfc3986\Uri::getUriType</methodname>
+ </member>
+ <member>
+ <methodname>Uri\WhatWg\Url::isSpecialScheme</methodname>
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection
-->
+ <member>
+ <methodname>Uri\Rfc3986\Uri::getHostType</methodname>
+ </member>
+ <member>
+ <methodname>Uri\WhatWg\Url::getHostType</methodname>
+ </member>
+ <!-- RFC: https://wiki.php.net/rfc/uri_followup#percent-encoding_support
-->
+ <member><function>Uri\WhatWg\url_percent_encode</function></member>
+ </simplelist>
+ </sect2>
+
+ <sect2 xml:id="migration86.new-functions.zip">
+ <title>Zip</title>
+
+ <simplelist>
+ <member>
+ <methodname>ZipArchive::openString</methodname>
+ </member>
+ <member>
+ <methodname>ZipArchive::closeString</methodname>
+ </member>
+ </simplelist>
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/other-changes.xml b/appendices/migration86/other-changes.xml
new file mode 100644
index 000000000000..3cb6af61bd59
--- /dev/null
+++ b/appendices/migration86/other-changes.xml
@@ -0,0 +1,585 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.other-changes">
+ <title>Other Changes</title>
+
+ <sect2 xml:id="migration86.other-changes.core">
+ <title>Core changes</title>
+
+ <sect3 xml:id="migration86.other-changes.core.core">
+ <title>Core</title>
+
+ <simpara>
+ In case of a hard <acronym>OOM</acronym> (out of memory) condition, PHP
+ now calls <literal>abort()</literal> instead of
<literal>exit(1)</literal>,
+ changing the exit code to 134 and possibly creating a core dump.
+ </simpara>
+
+ <simpara>
+ The <constant>PHP_OS_FAMILY</constant> constant has an
+ <literal>AIX</literal> value for when running on AIX or IBM i via PASE.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.other-changes.sapi">
+ <title>Changes in SAPI Modules</title>
+
+ <sect3 xml:id="migration86.other-changes.sapi.cli">
+ <title>CLI</title>
+
+ <simpara>
+ The <link linkend="features.commandline.webserver">built-in development
+ server</link> now accepts requests using the <acronym>HTTP</acronym>
+ <literal>QUERY</literal>
+ method instead of returning <literal>501 Not Implemented</literal>.
+ </simpara>
+
+ <simpara>
+ The <link linkend="features.commandline.webserver">built-in development
+ server</link> no longer reflects the <literal>Host</literal> header from
+ requests.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.other-changes.functions">
+ <title>Changed Functions</title>
+
+ <sect3 xml:id="migration86.other-changes.functions.core">
+ <title>Core</title>
+
+ <simpara>
+ <function>header_register_callback</function> now declares
+ <type>true</type> as its return type.
+ It has not been able to return &false; since PHP 8.0.0, when passing an
+ invalid callback started throwing a
+ <exceptionname>TypeError</exceptionname> instead.
+ </simpara>
+
+ <simpara>
+ <function>register_tick_function</function> now declares
+ <type>true</type> as its return type.
+ It has always returned &true; on success; an invalid callback throws a
+ <exceptionname>TypeError</exceptionname> during argument parsing before
+ the function body is reached.
+ </simpara>
+
+ <simpara>
+ <function>ini_get_all</function> now includes a
+ <literal>"builtin_default_value"</literal> element for each directive
+ when <parameter>details</parameter> is &true;.
+ It holds the built-in default value of the directive (or &null; if it
+ has none), independent of values set in &php.ini;, on the command line,
+ or at runtime.
+ </simpara>
+
+ <simpara>
+ <function>fclose</function>, <function>file_put_contents</function> and
+ <function>copy</function> now return &false; when flushing or closing the
+ stream fails.
+ Previously such failures were silently ignored.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.date">
+ <title>Date</title>
+
+ <para>
+ The following methods now have tentative <type>static</type> return
+ types:
+
+ <simplelist>
+
<member><methodname>DateTime::createFromInterface</methodname></member>
+
<member><methodname>DateTimeImmutable::__set_state</methodname></member>
+ <member><methodname>DateTimeImmutable::modify</methodname></member>
+ <member><methodname>DateTimeImmutable::add</methodname></member>
+ <member><methodname>DateTimeImmutable::sub</methodname></member>
+
<member><methodname>DateTimeImmutable::setTimezone</methodname></member>
+ <member><methodname>DateTimeImmutable::setTime</methodname></member>
+ <member><methodname>DateTimeImmutable::setDate</methodname></member>
+
<member><methodname>DateTimeImmutable::setISODate</methodname></member>
+
<member><methodname>DateTimeImmutable::setTimestamp</methodname></member>
+
<member><methodname>DateTimeImmutable::createFromInterface</methodname></member>
+ <member><methodname>DateTimeZone::__set_state</methodname></member>
+ <member><methodname>DateInterval::__set_state</methodname></member>
+ <member><methodname>DatePeriod::__set_state</methodname></member>
+ </simplelist>
+ </para>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.filter">
+ <title>Filter</title>
+
+ <simpara>
+ The return type of <function>filter_var_array</function> has been
+ narrowed from
+ <type
class="union"><type>array</type><type>false</type><type>null</type></type>
+ to <type
class="union"><type>array</type><type>false</type></type>.
+ The function always establishes an array before filtering, so &null; was
+ never returned.
+ <function>filter_input_array</function> is unaffected: it still returns
+ &null; when the requested superglobal does not exist.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.ldap">
+ <title>LDAP</title>
+
+ <simpara>
+ The return type of <function>ldap_free_result</function> has been
+ narrowed from <type>bool</type> to <type>true</type>.
+ The function already always returned &true;.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.mysqli">
+ <title>MySQLi</title>
+
+ <simpara>
+ The return structure of <function>mysqli_get_charset</function> no longer
+ contains the undocumented <literal>"comment"</literal> element.
+ The value of <literal>"charsetnr"</literal> is now set to a constant
+ <literal>0</literal> as this number was an implementation detail that
+ should not have been exposed to the public.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.openssl">
+ <title>OpenSSL</title>
+
+ <simpara>
+ The output of <function>openssl_x509_parse</function> now contains a
+ <literal>"criticalExtensions"</literal> entry listing all critical
+ certificate extensions.
+ </simpara>
+
+ <simpara>
+ <function>openssl_sign</function> and
<function>openssl_verify</function>
+ now have an additional optional <parameter>salt_length</parameter>
+ argument that allows controlling the <acronym>RSA</acronym>-PSS salt
+ length when
+ <constant>OPENSSL_PKCS1_PSS_PADDING</constant> is used.
+ It accepts an explicit length or one of the new
+
<constant>OPENSSL_RSA_PSS_SALTLEN_<replaceable>*</replaceable></constant>
+ constants.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.pdo-dblib">
+ <title>PDO_DBLIB</title>
+
+ <simpara>
+ When using persistent connections, there is now a liveness check in
+ <methodname>PDO::__construct</methodname>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.phar">
+ <title>Phar</title>
+
+ <simpara>
+ <methodname>Phar::mungServer</methodname> now supports reference values.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.readline">
+ <title>Readline</title>
+
+ <simpara>
+ <function>readline_completion_function</function> now declares
+ <type>true</type> as its return type.
+ It has always returned &true; on success; an invalid callback throws a
+ <exceptionname>TypeError</exceptionname> during argument parsing before
+ the function body is reached.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.reflection">
+ <title>Reflection</title>
+
+ <simpara>
+ The outputs of
+ <methodname>ReflectionClassConstant::__toString</methodname> and
+ <methodname>ReflectionConstant::__toString</methodname> now print
+ <type>bool</type> constant values as <literal>true</literal> and
+ <literal>false</literal> rather than <literal>1</literal> and an empty
+ string.
+ This also affects other ways of printing class constants, e.g.
+ <methodname>ReflectionClass::__toString</methodname>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.sockets">
+ <title>Sockets</title>
+
+ <simpara>
+ <function>socket_addrinfo_lookup</function> now has an additional
+ optional <parameter>error_code</parameter> argument that, when not
+ &null;, receives the error code on failure (one of the
+ <constant>EAI_<replaceable>*</replaceable></constant> constants).
+ </simpara>
+
+ <simpara>
+ The return type of <function>socket_cmsg_space</function> has been
+ narrowed from <type
class="union"><type>int</type><type>null</type></type>
+ to <type>int</type>.
+ Every failure path has thrown a
+ <exceptionname>ValueError</exceptionname> since PHP 8.0.0, so &null; was
+ never returned.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.functions.zip">
+ <title>Zip</title>
+
+ <simpara>
+ The return type of <function>zip_entry_close</function> has been narrowed
+ from <type>bool</type> to <type>true</type>.
+ The function already always returned &true;.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.other-changes.extensions">
+ <title>Other Changes to Extensions</title>
+
+ <sect3 xml:id="migration86.other-changes.extensions.fileinfo">
+ <title>Fileinfo</title>
+
+ <simpara>
+ Upgraded to file 5.48.
+ Custom compiled magic databases from older file versions must be
+ regenerated.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.extensions.hash">
+ <title>Hash</title>
+
+ <simpara>
+ The bundled version of xxHash was upgraded to 0.8.2.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.extensions.mysqli">
+ <title>MySQLi</title>
+
+ <simpara>
+ Added the new constant <constant>MYSQLI_OPT_COMPRESS</constant>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.extensions.opcache">
+ <title>Opcache</title>
+
+ <simpara>
+ <acronym>JIT</acronym> is now supported for <acronym>ZTS</acronym>
builds
+ on Apple Silicon.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.other-changes.ini">
+ <title>Changes to INI File Handling</title>
+
+ <sect3 xml:id="migration86.other-changes.ini.core">
+ <title>Core</title>
+
+ <simpara>
+ The <!-- <link linkend="ini.error-include-args">
-->error_include_args<!-- </link> -->
+ INI option has been added to make the display of function arguments
+ consistent in errors; it is off by default.
+ Previously, whether a function showed a parameter in the error was up to
+ each call to the internal error function.
+ Now, all parameters as they were actually passed to the function will be
+ displayed.
+ This uses the same infrastructure as stack traces, so sensitive
+ parameters will not be displayed, and strings will be truncated
+ according to
+ <link
linkend="ini.zend.exception-string-param-max-len">zend.exception_string_param_max_len</link>.
+ <!-- RFC: https://wiki.php.net/rfc/display_error_function_args
-->
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.ini.mbstring">
+ <title>MBString</title>
+
+ <simpara>
+ The <link
linkend="ini.mbstring.detect-order">mbstring.detect_order</link>
+ INI directive now updates the internal detection order when changed at
+ runtime via <function>ini_set</function>.
+ Previously, runtime changes using <function>ini_set</function> did not
+ take effect for <function>mb_detect_order</function>.
+ Setting the directive to &null; or an empty string at runtime now leaves
+ the previously configured detection order unchanged.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.ini.mysqli">
+ <title>MySQLi</title>
+
+ <simpara>
+ <link linkend="ini.mysqli.default-port">mysqli.default_port</link> now
+ checks the validity of the value, which should be between
+ <literal>0</literal> and <literal>65535</literal> inclusive.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.ini.opcache">
+ <title>Opcache</title>
+
+ <simpara>
+ <link linkend="ini.opcache.jit-debug">opcache.jit_debug</link> accepts a
+ new flag: <literal>ZEND_JIT_DEBUG_TRACE_EXIT_INFO_SRC</literal>.
+ When used along with <literal>ZEND_JIT_DEBUG_TRACE_EXIT_INFO</literal>,
+ the source of exit points is printed in the exit info output, in debug
+ builds.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.other-changes.performance">
+ <title>Performance</title>
+
+ <sect3 xml:id="migration86.other-changes.performance.core">
+ <title>Core</title>
+
+ <simpara>
+ <function>printf</function> using only <literal>%s</literal> and
+ <literal>%d</literal> will be compiled into the equivalent string
+ interpolation, avoiding the overhead of a function call and repeatedly
+ parsing the format string.
+ </simpara>
+
+ <simpara>
+ Arguments are now passed more efficiently to known constructors (e.g.
+ when using <code>new self()</code>).
+ </simpara>
+
+ <simpara>
+ <function>array_map</function> using a
+ <link linkend="functions.first_class_callable_syntax">first-class
+ callable</link> or
+ <link
linkend="migration86.new-features.core.partial-function-application">partial
+ function application</link> callback will be compiled into the equivalent
+ <link linkend="control-structures.foreach">foreach</link> loop, avoiding
the
+ creation of intermediate <classname>Closure</classname>s, the overhead of
+ calling userland callbacks from internal functions and providing for
+ better insight for the <acronym>JIT</acronym>.
+ </simpara>
+
+ <simpara>
+ The performance of the TAILCALL <acronym>VM</acronym> has been improved,
+ and it is now enabled on Windows when compiling with Clang 19 or later on
+ x86_64.
+ </simpara>
+
+ <simpara>
+ The performance of <acronym>ZTS</acronym> builds has been improved.
+ </simpara>
+
+ <simpara>
+ Added a stateless closure cache.
+ <!-- RFC: https://wiki.php.net/rfc/closure-optimizations#stateless_closure_caching
-->
+ </simpara>
+
+ <simpara>
+ Deeply recursive code that causes the VM to allocate new stack pages
+ should now be faster.
+ </simpara>
+
+ <simpara>
+ Improved performance of sorting single-element arrays.
+ </simpara>
+
+ <para>
+ Improved performance of the following functions:
+
+ <simplelist>
+ <member><function>addcslashes</function>, when generating octal
escapes</member>
+ <member><function>array_fill_keys</function></member>
+ <member><function>array_intersect</function></member>
+ <member><function>array_map</function>, with multiple arrays
passed</member>
+ <member><function>array_product</function> and
<function>array_sum</function>, for integer-only arrays</member>
+ <member><function>array_unshift</function></member>
+ <member><function>array_walk</function></member>
+ <member><function>intval</function>, for
<code>intval('0b...', 2)</code> and <code>intval('+0b...',
2)</code></member>
+ <member><function>pathinfo</function>, when requesting a single
component</member>
+ <member><function>str_pad</function></member>
+ <member><function>str_repeat</function>, when the multiplier is
<literal>1</literal></member>
+ <member><function>str_split</function></member>
+ </simplelist>
+ </para>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.dom">
+ <title>DOM</title>
+
+ <simpara>
+ Made <methodname>DOMText::splitText</methodname> and
+ <methodname>Dom\Text::splitText</methodname> faster and consume less
+ memory.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.gd">
+ <title>GD</title>
+
+ <simpara>
+ <function>imagebmp</function>, <function>imagewbmp</function>,
+ <function>imagegd</function>, and <function>imagegd2</function> now
+ buffer output when writing to PHP streams, significantly improving
+ performance when writing images to files.
+ </simpara>
+
+ <simpara>
+ Improved performance of <function>imagegrabscreen</function> and
+ <function>imagegrabwindow</function> on Windows.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.intl">
+ <title>Intl</title>
+
+ <simpara>
+ Improved performance of
+ <methodname>IntlCalendar::getAvailableLocales</methodname> and
+
<methodname>IntlDateFormatter::localtime</methodname>/<function>datefmt_localtime</function>
+ by pre-allocating their returned arrays.
+ </simpara>
+
+ <simpara>
+ Improved performance of <function>transliterator_list_ids</function> and
+ <function>resourcebundle_locales</function> by pre-allocating their
+ returned arrays.
+ </simpara>
+
+ <simpara>
+ Optimized callback invocation in
+ <methodname>IntlChar::enumCharTypes</methodname>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.json">
+ <title>JSON</title>
+
+ <simpara>
+ Improved performance of encoding arrays and objects.
+ </simpara>
+
+ <simpara>
+ Improved performance of indentation generation in
+ <function>json_encode</function> when using
+ <constant>JSON_PRETTY_PRINT</constant>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.mbstring">
+ <title>MBString</title>
+
+ <simpara>
+ Improved performance of <function>mb_str_pad</function>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.phar">
+ <title>Phar</title>
+
+ <simpara>
+ Reduced temporary allocations when iterating Phar directories.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.uri">
+ <title>URI</title>
+
+ <simpara>
+ Improved performance of <methodname>Uri\WhatWg\Url::parse</methodname>
+ when collecting validation errors by pre-allocating the error array.
+ </simpara>
+
+ <simpara>
+ Reduced allocations when reading IPv6/IPvFuture hosts and paths with
+ <classname>Uri\Rfc3986\Uri</classname>.
+ </simpara>
+
+ <simpara>
+ Improved performance and memory consumption when using normalizing
+ (non-raw) getters on already-normalized URIs with
+ <classname>Uri\Rfc3986\Uri</classname>.
+ </simpara>
+
+ </sect3>
+
+ <sect3 xml:id="migration86.other-changes.performance.zip">
+ <title>Zip</title>
+
+ <simpara>
+ Improved performance of <methodname>ZipArchive::addGlob</methodname> and
+ <methodname>ZipArchive::addPattern</methodname> by pre-allocating their
+ returned arrays.
+ </simpara>
+
+ <simpara>
+ Avoid string copies in <methodname>ZipArchive::addFromString</methodname>.
+ </simpara>
+
+ </sect3>
+
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/appendices/migration86/windows-support.xml b/appendices/migration86/windows-support.xml
new file mode 100644
index 000000000000..ee6aea1774bf
--- /dev/null
+++ b/appendices/migration86/windows-support.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<sect1 xml:id="migration86.windows-support">
+ <title>Windows Support</title>
+
+ <sect2 xml:id="migration86.windows-support.core">
+ <title>Core</title>
+
+ <simpara>
+ The official Windows builds now use Visual Studio 2026 (VS18).
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.windows-support.libxml">
+ <title>LibXML</title>
+
+ <simpara>
+ The libxml2 library used by the official Windows builds has been upgraded
+ to version 2.15.3.
+ As a result, <property>DOMDocument::$documentURI</property> for documents
+ loaded from a local file now contains a native filesystem path instead of
+ a file <acronym>URI</acronym>.
+ </simpara>
+
+ </sect2>
+
+ <sect2 xml:id="migration86.windows-support.openssl">
+ <title>OpenSSL</title>
+
+ <simpara>
+ The OpenSSL library used by the official Windows builds has been upgraded
+ to OpenSSL 4.
+ </simpara>
+
+ </sect2>
+
+</sect1>
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"~/.phpdoc/manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
diff --git a/manual.xml b/manual.xml
index f393b9f67120..d9302073d28d 100644
--- a/manual.xml
+++ b/manual.xml
@@ -412,6 +412,7 @@
<title>&Appendices;</title>
&appendices.history;
&appendices.examples;
+ &appendices.migration86;
&appendices.migration85;
&appendices.migration84;
&appendices.migration83;