[DOC-CVS] [doc-en] master: errorfunc: document the error handler stack (#5253)

[email protected] (Louis-Arnaud via GitHub)
Newsgroups php.doc.cvs
Message-ID <[email protected]>
Author: Louis-Arnaud (lacatoire)
Committer: GitHub (web-flow)
Pusher: lacatoire
Date: 2026-08-25T22:16:49+02:00

Commit: https://github.com/php/doc-en/commit/cbfc5d5d55d682c185583561295462a6d5ca0ea1
Raw diff: https://github.com/php/doc-en/commit/cbfc5d5d55d682c185583561295462a6d5ca0ea1.diff

errorfunc: document the error handler stack (#5253)

set_error_handler() saves the handler that was active until then on an
internal stack and installs the new one in its place;
restore_error_handler() pops the saved one back, together with the
error_levels mask it was registered with. Neither page said so, which
invites restoring the previous handler by passing the return value of
set_error_handler() back to it. That grows the stack instead of
shrinking it, and resets error_levels to E_ALL.

Adds a caution against that pattern and an example calling the previous
handler from the new one. The seealso simplelists are unwrapped from
their para, matching the exception handler pages.

Changed paths:
  M  reference/errorfunc/functions/restore-error-handler.xml
  M  reference/errorfunc/functions/set-error-handler.xml


Diff:

diff --git a/reference/errorfunc/functions/restore-error-handler.xml b/reference/errorfunc/functions/restore-error-handler.xml
index 31cbc6c532bb..a374b5de3820 100644
--- a/reference/errorfunc/functions/restore-error-handler.xml
+++ b/reference/errorfunc/functions/restore-error-handler.xml
@@ -12,11 +12,17 @@
    <type>true</type><methodname>restore_error_handler</methodname>
    <void/>
   </methodsynopsis>
-  <para>
-   Used after changing the error handler function using
-   <function>set_error_handler</function>, to revert to the previous error
-   handler (which could be the built-in or a user defined function). 
-  </para>
+  <simpara>
+   Pops the error handler that was active before the most recent call to
+   <function>set_error_handler</function> off the internal stack of error
+   handlers and makes it active again, together with the
+   <parameter>error_levels</parameter> mask it was registered with.
+   The restored handler could be the built-in or a user defined function.
+  </simpara>
+  <simpara>
+   Calling this function more often than <function>set_error_handler</function>
+   leaves the built-in error handler active; no error is raised.
+  </simpara>
  </refsect1>
 
  <refsect1 role="parameters">
@@ -67,15 +73,13 @@ Invalid serialized value.
 
  <refsect1 role="seealso">
   &reftitle.seealso;
-  <para>
-   <simplelist>
-    <member><function>error_reporting</function></member>
-    <member><function>set_error_handler</function></member>
-    <member><function>get_error_handler</function></member>
-    <member><function>restore_exception_handler</function></member>
-    <member><function>trigger_error</function></member>
-   </simplelist>
-  </para>
+  <simplelist>
+   <member><function>error_reporting</function></member>
+   <member><function>set_error_handler</function></member>
+   <member><function>get_error_handler</function></member>
+   <member><function>restore_exception_handler</function></member>
+   <member><function>trigger_error</function></member>
+  </simplelist>
  </refsect1>
 </refentry>
 <!-- Keep this comment at the end of the file
diff --git a/reference/errorfunc/functions/set-error-handler.xml b/reference/errorfunc/functions/set-error-handler.xml
index f425fd317229..aa620d26cc20 100644
--- a/reference/errorfunc/functions/set-error-handler.xml
+++ b/reference/errorfunc/functions/set-error-handler.xml
@@ -13,10 +13,13 @@
    <methodparam><type class="union"><type>callable</type><type>null</type></type><parameter>callback</parameter></methodparam>
    <methodparam choice="opt"><type>int</type><parameter>error_levels</parameter><initializer><constant>E_ALL</constant></initializer></methodparam>
   </methodsynopsis>
-  <para>
-   Sets a user function (<parameter>callback</parameter>) to handle
-   errors in a script.
-  </para>
+  <simpara>
+   Sets a user function (<parameter>callback</parameter>) to handle errors in
+   a script.
+   The handler that was active until then is saved on an internal stack of
+   error handlers, from which <function>restore_error_handler</function> pops
+   it back.
+  </simpara>
   <para>
    This function can be used to define custom error handlers during runtime,
    for example in applications which need to do file/data cleanup when a critical
@@ -62,10 +65,13 @@
     <varlistentry>
      <term><parameter>callback</parameter></term>
      <listitem>
-      <para>
-       If &null; is passed, the handler is reset to its default state.
+      <simpara>
+       If &null; is passed, no user-defined handler is left active and errors
+       fall back to the built-in error handler.
+       The handler that was active until then is still saved on the stack and
+       can be brought back with <function>restore_error_handler</function>.
        Otherwise, the handler is a callback with the following signature:
-      </para>
+      </simpara>
       <para>
        <methodsynopsis>
         <type>bool</type><methodname><replaceable>handler</replaceable></methodname>
@@ -329,24 +335,83 @@ vector d - fatal error
 <b>My ERROR</b> [256] log(x) for x <= 0 is undefined, you used: scale = -2.5<br />
   Fatal error on line 35 in file trigger_error.php, PHP 5.2.1 (FreeBSD)<br />
 Aborting...<br />
+]]>
+    </screen>
+   </example>
+  </para>
+  <para>
+   <example>
+    <title>Calling the previous handler from the new one</title>
+    <simpara>
+     The return value of <function>set_error_handler</function> is the handler
+     that was active until then. Calling it from the new handler keeps the
+     existing behaviour while adding to it. This only works if that handler is
+     a user defined function; &null; is returned when the built-in one was
+     active.
+    </simpara>
+    <programlisting role="php">
+<![CDATA[
+<?php
+function logging_handler(int $errno, string $errstr): bool
+{
+    echo "logged: $errstr\n";
+    return true;
+}
+
+set_error_handler('logging_handler');
+
+$previous = set_error_handler(function (int $errno, string $errstr) use (&$previous): bool {
+    echo "counted: $errstr\n";
+    return $previous !== null ? $previous($errno, $errstr) : false;
+});
+
+trigger_error("something happened", E_USER_WARNING);
+
+restore_error_handler();
+trigger_error("only logged now", E_USER_WARNING);
+
+restore_error_handler();
+?>
+]]>
+    </programlisting>
+    &example.outputs;
+    <screen>
+<![CDATA[
+counted: something happened
+logged: something happened
+logged: only logged now
 ]]>
     </screen>
    </example>
   </para>
  </refsect1>
 
+ <refsect1 role="notes">
+  &reftitle.notes;
+  <caution>
+   <simpara>
+    Do not revert to the previous handler by passing the return value of
+    <function>set_error_handler</function> back to it.
+    Doing so pushes a further entry onto the stack instead of removing one,
+    so the stack grows without bound in loops and long-running processes, and
+    <parameter>error_levels</parameter> is reset to <constant>E_ALL</constant>,
+    silently widening the range of errors the restored handler receives.
+    <function>restore_error_handler</function> is the intended way to revert
+    to the previous handler.
+   </simpara>
+  </caution>
+ </refsect1>
+
  <refsect1 role="seealso">
   &reftitle.seealso;
-  <para>
-   <simplelist>
-    <member><classname>ErrorException</classname></member>
-    <member><function>error_reporting</function></member>
-    <member><function>restore_error_handler</function></member>
-    <member><function>get_error_handler</function></member>
-    <member><function>trigger_error</function></member>
-    <member><link linkend="errorfunc.constants">error level constants</link></member>
-   </simplelist>
-  </para>
+  <simplelist>
+   <member><exceptionname>ErrorException</exceptionname></member>
+   <member><function>error_reporting</function></member>
+   <member><function>restore_error_handler</function></member>
+   <member><function>get_error_handler</function></member>
+   <member><function>trigger_error</function></member>
+   <member><link linkend="errorfunc.constants">error level constants</link></member>
+  </simplelist>
  </refsect1>
 </refentry>
 <!-- Keep this comment at the end of the file
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.