Skip to content

OPcache bypasses the user-defined error handler for deprecations #17422

Closed

Description

@dtdesign

Description

The OPcache bypasses the user-defined error handler for deprecations for the first request with a cold cache. This happens regardless of opcache.record-warnings which only has an impact on all following requests. Enabling opcache.record-warnings will yield the expected behavior for every request after the first one which populated the cache.

This does not happen when OPcache is disabled, it will behave properly for every request.

test.php

<?php

set_error_handler(static fn (...$params) => var_dump($params));

require "./dependency.php";

implicitly_nullable_parameter(new stdClass());

dependency.php

<?php
function implicitly_nullable_parameter(stdClass $foo = null) {
    echo "Called\n";
}

This can be reproduced on a web server whenever opcache_reset() reset is invoked but for simplicity this was also verified to be triggered via CLI (thanks to @TimWolla for this suggestion):

$> php -dopcache.enable_cli=1 --repeat 2 test.php
Executing for the first time...
PHP Deprecated:  implicitly_nullable_parameter(): Implicitly marking parameter $foo as nullable is deprecated, the explicit nullable type must be used instead in /opt/homebrew/var/www/test/opcache-deprecations/dependency.php on line 2

Deprecated: implicitly_nullable_parameter(): Implicitly marking parameter $foo as nullable is deprecated, the explicit nullable type must be used instead in /opt/homebrew/var/www/test/opcache-deprecations/dependency.php on line 2
Called
Finished execution, repeating...
Called
$> php --version
PHP 8.4.2 (cli) (built: Dec 17 2024 15:31:31) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.2, Copyright (c), by Zend Technologies

(Note: There is a bonus bug hiding here, the deprecation message is printed twice. However, the first message starts with PHP Deprecated: (…) but the second message only reads Deprecated: (…).)

The script was also invoked manually via CLI without OPcache to verify that the behavior is identical to FPM:

$> php test.php
array(4) {
  [0]=>
  int(8192)
  [1]=>
  string(141) "implicitly_nullable_parameter(): Implicitly marking parameter $foo as nullable is deprecated, the explicit nullable type must be used instead"
*snip*

PHP Version

PHP 8.4.2

Operating System

macOS 15.1.1

Activity

  1. TimWolla commented on Jan 10, 2025

    @TimWolla
    Member

    OPcache explicitly clears the user error handler in:

    ZVAL_UNDEF(&EG(user_error_handler));

    before compiling the file. This is done since the first version of the open source OPcache, so it's not clear to me, why it does this.

  2. tomasjanicek commented on Jan 16, 2025

    @tomasjanicek

    I have same problem on php version 8.4.1

  3. kkmuffme commented on Jan 17, 2025

    @kkmuffme

    Works correctly for me (Rocky Linux)

  4. jmglsn commented on Jan 20, 2025

    @jmglsn

    Bug is still present on PHP 8.4.3.

  5. dtdesign commented on Jan 29, 2025

    @dtdesign
    Author

    (Note: There is a bonus bug hiding here, the deprecation message is printed twice. However, the first message starts with PHP Deprecated: (…) but the second message only reads Deprecated: (…).)

    I stand corrected, this is caused by log_errors being enabled, thanks to @TimWolla for pointing this out:

    $> php -dopcache.enable_cli=1 -dlog_errors=0 --repeat 2 test.php
    Executing for the first time...
    
    Deprecated: implicitly_nullable_parameter(): Implicitly marking parameter $foo as nullable is deprecated, the explicit nullable type must be used instead in /opt/homebrew/var/www/test/opcache-deprecations/dependency.php on line 2
    Called
    Finished execution, repeating...
    Called
    
  6. added theissue type on Feb 5, 2025
  7. DylanGD commented on Feb 25, 2025

    @DylanGD

    The bug is still present on PHP 8.4.4

  8. kbalashov1 commented on Mar 13, 2025

    @kbalashov1

    Confirming seeing this issue, that specific deprecation ignoring our error handlers messes up our json outputs and blocks us from upgrading to 8.4. Fixing all deprecations isn't an option, as we can do so in our own code but not in the packages that we use.

  9. umpirsky commented on Apr 9, 2025

    @umpirsky

    The bug is still present on PHP 8.4.5.

  10. aszenz commented on Apr 9, 2025

    @aszenz

    I can confirm this issue is present on our symfony app

  11. lewischadwick commented on Apr 16, 2025

    @lewischadwick

    Confirmed this still happens on PHP 8.4.6 too.

  12. added a commit that references this issue on May 12, 2025
    731db8e
  13. vencakrecl commented on May 14, 2025

    @vencakrecl

    Is it still the same on PHP 8.4.7?

  14. boewamwinter commented on May 21, 2025

    @boewamwinter

    Is it still the same on PHP 8.4.7?

    Yes it is for me.

  15. added a commit that references this issue on Jun 13, 2025
  16. Hanashi commented on Jun 24, 2025

    @Hanashi

    Bug is still present in PHP 8.4.8

  17. added a commit that references this issue on Jul 27, 2025
    7b3e68f
  18. arnaud-lb commented on Jul 27, 2025

    @arnaud-lb
    Member

    This has been fixed in version 8.5. Unfortunately this can not be fixed in earlier versions due to complexity and breaking changes.

    A possible workaround for 8.4, when a user-defined error handler is in place, is to remove E_DEPRECATED from error_reporting: symfony/symfony@9a7f7a3. In effect, this will prevent displaying/logging E_DEPRECATED errors entirely (including during compilation), but will still call the user-defined error handlers (except during compilation): https://gist.github.com/arnaud-lb/47bc1e93f01e6740e2dbbc2e44b27f3d.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions