diff --git a/NEWS b/NEWS index e498fac1d82ef..48e71a0a80632 100644 --- a/NEWS +++ b/NEWS @@ -169,6 +169,8 @@ PHP NEWS - Standard: . Fixed crypt() tests on musl when using --with-external-libcrypt (Michael Orlitzky). + . Fixed bug GH-18062 (is_callable(func(...), callable_name: $name) for first + class callables returns wrong name). (timwolla) - Streams: . Fixed bug GH-16889 (stream_select() timeout useless for pipes on Windows). diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index b963ead5cc8da..d2ab4e13fc52c 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -19,6 +19,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES a value to a non-reference zval. . Added zval_ptr_safe_dtor() to safely destroy a zval when a destructor could interfere. + . zend_get_callable_name() now returns the name of the underlying function + for fake closures. ======================== 2. Build system changes diff --git a/Zend/tests/first_class_callable/gh18062.phpt b/Zend/tests/first_class_callable/gh18062.phpt new file mode 100644 index 0000000000000..3865dd77adba8 --- /dev/null +++ b/Zend/tests/first_class_callable/gh18062.phpt @@ -0,0 +1,32 @@ +--TEST-- +First Class Callable returns correct name from is_callable() +--FILE-- + +--EXPECT-- +string(9) "some_func" +string(13) "Foo::__invoke" +string(8) "Foo::bar" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 162f9aced147b..2be3d428270f2 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4134,6 +4134,19 @@ ZEND_API zend_string *zend_get_callable_name_ex(zval *callable, zend_object *obj case IS_OBJECT: { zend_class_entry *ce = Z_OBJCE_P(callable); + + if (ce == zend_ce_closure) { + const zend_function *fn = zend_get_closure_method_def(Z_OBJ_P(callable)); + + if (fn->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) { + if (fn->common.scope) { + return zend_create_member_string(fn->common.scope->name, fn->common.function_name); + } else { + return zend_string_copy(fn->common.function_name); + } + } + } + return zend_string_concat2( ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), "::__invoke", sizeof("::__invoke") - 1); diff --git a/ext/standard/tests/file/bug72666_variation3.phpt b/ext/standard/tests/file/bug72666_variation3.phpt index a491640c4f746..7937bd904ad7d 100644 --- a/ext/standard/tests/file/bug72666_variation3.phpt +++ b/ext/standard/tests/file/bug72666_variation3.phpt @@ -5,23 +5,11 @@ Bug #72666 (stat cache clearing inconsistent - plain wrapper) $filename = __DIR__ . '/bug72666_variation3.txt'; file_put_contents($filename, "test"); -$fd = fopen($filename, "r"); -$atime1 = fileatime($filename); -sleep(1); -var_dump(fread($fd, 4)); -$atime2 = fileatime($filename); $mtime1 = filemtime($filename); -fclose($fd); $fd = fopen($filename, "w"); sleep(1); var_dump(fwrite($fd, "data")); $mtime2 = filemtime($filename); -if (substr(PHP_OS, 0, 3) == 'WIN') { - // Windows do not hundle atime - var_dump($atime2 == $atime1); -} else { - var_dump($atime2 > $atime1); -} var_dump($mtime2 > $mtime1); ?> --CLEAN-- @@ -29,7 +17,5 @@ var_dump($mtime2 > $mtime1); unlink(__DIR__ . '/bug72666_variation3.txt'); ?> --EXPECT-- -string(4) "test" int(4) bool(true) -bool(true) diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c index d93459b4ea370..fb02a3e191775 100644 --- a/sapi/fpm/fpm/fpm_php.c +++ b/sapi/fpm/fpm/fpm_php.c @@ -93,7 +93,21 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */ if (!strcmp(name, "extension") && *value) { zval zv; zend_interned_strings_switch_storage(0); + +#if ZEND_RC_DEBUG + bool orig_rc_debug = zend_rc_debug; + /* Loading extensions after php_module_startup() breaks some invariants. + * For instance, it will update the refcount of persistent strings, + * which is normally not allowed at this stage. */ + zend_rc_debug = false; +#endif + php_dl(value, MODULE_PERSISTENT, &zv, 1); + +#if ZEND_RC_DEBUG + zend_rc_debug = orig_rc_debug; +#endif + zend_interned_strings_switch_storage(1); return Z_TYPE(zv) == IS_TRUE ? FPM_PHP_INI_EXTENSION_LOADED : FPM_PHP_INI_EXTENSION_FAILED; }