Skip to content

Fix error handling during opcache compilation #18541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Unify non-opcache behavior
  • Loading branch information
arnaud-lb committed May 13, 2025
commit 4338079d4da2f3b651aec2d0c1b1898b5ce8d276
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
Deprecation promoted to exception during inheritance
--SKIPIF--
<?php
if (getenv('SKIP_PRELOAD')) die('skip Error handler not active during preloading');
?>
--FILE--
<?php

set_error_handler(function($code, $message) {
throw new Exception($message);
});

try {
class C extends DateTime {
public function getTimezone() {}
public function getTimestamp() {}
};
} catch (Exception $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}

var_dump(new C());

?>
--EXPECTF--
Exception: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
object(C)#%d (3) {
["date"]=>
string(%d) "%s"
["timezone_type"]=>
int(3)
["timezone"]=>
string(3) "UTC"
}
32 changes: 21 additions & 11 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -3543,8 +3543,6 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
}
#endif

bool orig_record_errors = EG(record_errors);

if (ce->ce_flags & ZEND_ACC_IMMUTABLE && is_cacheable) {
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces);
Expand All @@ -3556,16 +3554,21 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
Z_CE_P(zv) = ret;
return ret;
}

/* Make sure warnings (such as deprecations) thrown during inheritance
* will be recorded in the inheritance cache. */
zend_begin_record_errors();
} else {
is_cacheable = 0;
}
proto = ce;
}

/* Delay and record warnings (such as deprecations) thrown during
* inheritance, so they will be recorded in the inheritance cache.
* Warnings must be delayed in all cases so that we get a consistent
* behavior regardless of cacheability. */
bool orig_record_errors = EG(record_errors);
if (!orig_record_errors) {
zend_begin_record_errors();
}

zend_try {
if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
/* Lazy class loading */
Expand Down Expand Up @@ -3917,10 +3920,12 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
orig_linking_class = CG(current_linking_class);
CG(current_linking_class) = is_cacheable ? ce : NULL;

bool orig_record_errors = EG(record_errors);

zend_try{
CG(zend_lineno) = ce->info.user.line_start;

if (is_cacheable) {
if (!orig_record_errors) {
zend_begin_record_errors();
}

Expand All @@ -3942,13 +3947,13 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_

CG(current_linking_class) = orig_linking_class;
} zend_catch {
EG(record_errors) = false;
zend_free_recorded_errors();
if (!orig_record_errors) {
EG(record_errors) = false;
zend_free_recorded_errors();
}
zend_bailout();
} zend_end_try();

EG(record_errors) = false;

if (is_cacheable) {
HashTable *ht = (HashTable*)ce->inheritance_cache;
zend_class_entry *new_ce;
Expand All @@ -3966,6 +3971,11 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
}
}

if (!orig_record_errors) {
zend_emit_recorded_errors();
zend_free_recorded_errors();
}

if (ZSTR_HAS_CE_CACHE(ce->name)) {
ZSTR_SET_CE_CACHE(ce->name, ce);
}
Expand Down
10 changes: 10 additions & 0 deletions Zend/zend_language_scanner.l
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,17 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type)
}
}
} else {
bool orig_record_errors = EG(record_errors);
if (!orig_record_errors) {
zend_begin_record_errors();
}

op_array = zend_compile(ZEND_USER_FUNCTION);

if (!orig_record_errors) {
zend_emit_recorded_errors();
zend_free_recorded_errors();
}
}

zend_restore_lexical_state(&original_lex_state);
Expand Down
15 changes: 0 additions & 15 deletions ext/opcache/tests/gh17422/001.phpt
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations)
--INI--
opcache.enable=1
opcache.enable_cli=1
--EXTENSIONS--
opcache
--FILE--
<?php

require __DIR__ . "/shutdown.inc";

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});
Expand All @@ -22,11 +15,3 @@ warning();
--EXPECT--
set_error_handler: "continue" targeting switch is equivalent to "break"
OK: warning
array(3) {
[0]=>
string(7) "001.php"
[1]=>
string(12) "shutdown.inc"
[2]=>
string(11) "warning.inc"
}
15 changes: 0 additions & 15 deletions ext/opcache/tests/gh17422/002.phpt
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Throwing error handler
--INI--
opcache.enable=1
opcache.enable_cli=1
--EXTENSIONS--
opcache
--FILE--
<?php

require __DIR__ . "/shutdown.inc";

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
});
Expand All @@ -26,11 +19,3 @@ warning();
--EXPECT--
Caught: "continue" targeting switch is equivalent to "break"
OK: warning
array(3) {
[0]=>
string(7) "002.php"
[1]=>
string(12) "shutdown.inc"
[2]=>
string(11) "warning.inc"
}
21 changes: 3 additions & 18 deletions ext/opcache/tests/gh17422/003.phpt
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal Error
--INI--
opcache.enable=1
opcache.enable_cli=1
memory_limit=2M
--EXTENSIONS--
opcache
--FILE--
<?php

require __DIR__ . "/shutdown.inc";

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
str_repeat('x', 1024 * 1024 * 1024);
function fatal_error() {}
function fatal_error() {}
});

require __DIR__ . "/warning.inc";
Expand All @@ -21,12 +14,4 @@ warning();

?>
--EXPECTF--
Fatal error: Allowed memory size of 2097152 bytes exhausted %s on line 6
array(3) {
[0]=>
string(7) "003.php"
[1]=>
string(12) "shutdown.inc"
[2]=>
string(11) "warning.inc"
}
Fatal error: Cannot redeclare function fatal_error() (previously declared in %s:%d) in %s on line %d
18 changes: 1 addition & 17 deletions ext/opcache/tests/gh17422/004.phpt
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - eval
--INI--
opcache.enable=1
opcache.enable_cli=1
memory_limit=2M
--EXTENSIONS--
opcache
--FILE--
<?php

require __DIR__ . "/shutdown.inc";

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
eval(
<<<'PHP'
Expand All @@ -27,12 +19,4 @@ warning();

?>
--EXPECTF--
Fatal error: Cannot redeclare %Swarning() (previously declared in %s(8) : eval()'d code:1) in %swarning.inc on line 2
array(3) {
[0]=>
string(7) "004.php"
[1]=>
string(12) "shutdown.inc"
[2]=>
string(11) "warning.inc"
}
Fatal error: Cannot redeclare function warning() %s
18 changes: 0 additions & 18 deletions ext/opcache/tests/gh17422/005.phpt
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - require
--INI--
opcache.enable=1
opcache.enable_cli=1
memory_limit=2M
--EXTENSIONS--
opcache
--FILE--
<?php

require __DIR__ . "/shutdown.inc";

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
require_once __DIR__ . "/dummy.inc";
});
Expand All @@ -22,13 +14,3 @@ dummy();
?>
--EXPECT--
OK: dummy
array(4) {
[0]=>
string(7) "005.php"
[1]=>
string(9) "dummy.inc"
[2]=>
string(12) "shutdown.inc"
[3]=>
string(11) "warning.inc"
}
25 changes: 25 additions & 0 deletions ext/opcache/tests/gh17422/006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - File cache
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache={TMP}
opcache.file_cache_only=1
opcache.record_warnings=1
--EXTENSIONS--
opcache
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/warning.inc";

warning();

?>
--EXPECT--
set_error_handler: "continue" targeting switch is equivalent to "break"
OK: warning
18 changes: 18 additions & 0 deletions ext/opcache/tests/gh17422/007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Fatal after warning
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/warning-fatal.inc";

warning();

?>
--EXPECTF--
Warning: "continue" targeting switch is equivalent to "break" in %s on line %d

Fatal error: Cannot redeclare function warning() (previously declared in %s:%d) in %s on line %d
14 changes: 14 additions & 0 deletions ext/opcache/tests/gh17422/008.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding warning
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/early-bind-warning.inc";

?>
--EXPECTF--
set_error_handler: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
16 changes: 16 additions & 0 deletions ext/opcache/tests/gh17422/009.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Early binding error after warning
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/early-bind-warning-error.inc";

?>
--EXPECTF--
Deprecated: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s on line %d

Fatal error: Declaration of C::getTimestamp(C $arg): int must be compatible with DateTime::getTimestamp(): int in %s on line %d
14 changes: 14 additions & 0 deletions ext/opcache/tests/gh17422/010.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance warning
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/link-warning.inc";

?>
--EXPECTF--
set_error_handler: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
16 changes: 16 additions & 0 deletions ext/opcache/tests/gh17422/011.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-17422 (OPcache bypasses the user-defined error handler for deprecations) - Inheritance error after warning
--FILE--
<?php

set_error_handler(static function (int $errno, string $errstr, string $errfile, int $errline) {
echo "set_error_handler: {$errstr}", PHP_EOL;
});

require __DIR__ . "/link-warning-error.inc";

?>
--EXPECTF--
Deprecated: Return type of C::getTimezone() should either be compatible with DateTime::getTimezone(): DateTimeZone|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in %s on line %d

Fatal error: Declaration of C::getTimestamp(C $arg): int must be compatible with DateTime::getTimestamp(): int %s on line %d
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy