Refactor register shutdown function mechanism

Use FCI/FCC structure instead of custom implementation which does the same.
This also fixes the "bug" which prevented static methods from being shutdown functions.

Closes GH-5829

Co-authored-by: Aaron Piotrowski <aaron@trowski.com>
This commit is contained in:
George Peter Banyard 2021-05-10 12:05:41 +01:00
parent fcd18757b2
commit a9695cc615
No known key found for this signature in database
GPG key ID: D49A095D7329F6DC
5 changed files with 39 additions and 50 deletions

View file

@ -12,7 +12,7 @@ class try_class
static public function on_shutdown () static public function on_shutdown ()
{ {
printf ("CHECKPOINT\n"); /* never reached */ printf ("CHECKPOINT\n");
} }
} }
@ -22,5 +22,4 @@ echo "Done\n";
?> ?>
--EXPECT-- --EXPECT--
Done Done
CHECKPOINT
Fatal error: Registered shutdown function self::on_shutdown() cannot be called, function does not exist in Unknown on line 0

View file

@ -2047,13 +2047,18 @@ PHP_FUNCTION(session_set_save_handler)
if (register_shutdown) { if (register_shutdown) {
/* create shutdown function */ /* create shutdown function */
php_shutdown_function_entry shutdown_function_entry; php_shutdown_function_entry shutdown_function_entry;
ZVAL_STRING(&shutdown_function_entry.function_name, "session_register_shutdown"); zval callable;
shutdown_function_entry.arg_count = 0; zend_result result;
shutdown_function_entry.arguments = NULL;
ZVAL_STRING(&callable, "session_register_shutdown");
result = zend_fcall_info_init(&callable, 0, &shutdown_function_entry.fci,
&shutdown_function_entry.fci_cache, NULL, NULL);
ZEND_ASSERT(result == SUCCESS);
/* add shutdown function, removing the old one if it exists */ /* add shutdown function, removing the old one if it exists */
if (!register_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1, &shutdown_function_entry)) { if (!register_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1, &shutdown_function_entry)) {
zval_ptr_dtor(&shutdown_function_entry.function_name); zval_ptr_dtor(&callable);
php_error_docref(NULL, E_WARNING, "Unable to register session shutdown function"); php_error_docref(NULL, E_WARNING, "Unable to register session shutdown function");
RETURN_FALSE; RETURN_FALSE;
} }
@ -2654,6 +2659,8 @@ PHP_FUNCTION(session_status)
PHP_FUNCTION(session_register_shutdown) PHP_FUNCTION(session_register_shutdown)
{ {
php_shutdown_function_entry shutdown_function_entry; php_shutdown_function_entry shutdown_function_entry;
zval callable;
zend_result result;
ZEND_PARSE_PARAMETERS_NONE(); ZEND_PARSE_PARAMETERS_NONE();
@ -2663,13 +2670,14 @@ PHP_FUNCTION(session_register_shutdown)
* function after calling session_set_save_handler(), which expects * function after calling session_set_save_handler(), which expects
* the session still to be available. * the session still to be available.
*/ */
ZVAL_STRING(&callable, "session_write_close");
result = zend_fcall_info_init(&callable, 0, &shutdown_function_entry.fci,
&shutdown_function_entry.fci_cache, NULL, NULL);
ZVAL_STRING(&shutdown_function_entry.function_name, "session_write_close"); ZEND_ASSERT(result == SUCCESS);
shutdown_function_entry.arg_count = 0;
shutdown_function_entry.arguments = NULL;
if (!append_user_shutdown_function(&shutdown_function_entry)) { if (!append_user_shutdown_function(&shutdown_function_entry)) {
zval_ptr_dtor(&shutdown_function_entry.function_name); zval_ptr_dtor(&callable);
/* Unable to register shutdown function, presumably because of lack /* Unable to register shutdown function, presumably because of lack
* of memory, so flush the session now. It would be done in rshutdown * of memory, so flush the session now. It would be done in rshutdown

View file

@ -1664,14 +1664,10 @@ PHP_FUNCTION(forward_static_call_array)
void user_shutdown_function_dtor(zval *zv) /* {{{ */ void user_shutdown_function_dtor(zval *zv) /* {{{ */
{ {
int i;
php_shutdown_function_entry *shutdown_function_entry = Z_PTR_P(zv); php_shutdown_function_entry *shutdown_function_entry = Z_PTR_P(zv);
zval_ptr_dtor(&shutdown_function_entry->function_name); zval_ptr_dtor(&shutdown_function_entry->fci.function_name);
for (i = 0; i < shutdown_function_entry->arg_count; i++) { zend_fcall_info_args_clear(&shutdown_function_entry->fci, true);
zval_ptr_dtor(&shutdown_function_entry->arguments[i]);
}
efree(shutdown_function_entry->arguments);
efree(shutdown_function_entry); efree(shutdown_function_entry);
} }
/* }}} */ /* }}} */
@ -1687,22 +1683,14 @@ static int user_shutdown_function_call(zval *zv) /* {{{ */
{ {
php_shutdown_function_entry *shutdown_function_entry = Z_PTR_P(zv); php_shutdown_function_entry *shutdown_function_entry = Z_PTR_P(zv);
zval retval; zval retval;
zend_result call_status;
if (!zend_is_callable(&shutdown_function_entry->function_name, 0, NULL)) { /* set retval zval for FCI struct */
zend_string *function_name = zend_get_callable_name(&shutdown_function_entry->function_name); shutdown_function_entry->fci.retval = &retval;
zend_throw_error(NULL, "Registered shutdown function %s() cannot be called, function does not exist", ZSTR_VAL(function_name)); call_status = zend_call_function(&shutdown_function_entry->fci, &shutdown_function_entry->fci_cache);
zend_string_release(function_name); ZEND_ASSERT(call_status == SUCCESS);
return 0;
}
if (call_user_function(NULL, NULL,
&shutdown_function_entry->function_name,
&retval,
shutdown_function_entry->arg_count,
shutdown_function_entry->arguments) == SUCCESS)
{
zval_ptr_dtor(&retval); zval_ptr_dtor(&retval);
}
return 0; return 0;
} }
/* }}} */ /* }}} */
@ -1761,8 +1749,7 @@ PHPAPI void php_call_shutdown_functions(void) /* {{{ */
if (BG(user_shutdown_function_names)) { if (BG(user_shutdown_function_names)) {
zend_try { zend_try {
zend_hash_apply(BG(user_shutdown_function_names), user_shutdown_function_call); zend_hash_apply(BG(user_shutdown_function_names), user_shutdown_function_call);
} } zend_end_try();
zend_end_try();
} }
} }
/* }}} */ /* }}} */
@ -1786,23 +1773,19 @@ PHPAPI void php_free_shutdown_functions(void) /* {{{ */
PHP_FUNCTION(register_shutdown_function) PHP_FUNCTION(register_shutdown_function)
{ {
php_shutdown_function_entry entry; php_shutdown_function_entry entry;
zend_fcall_info fci; zval *params = NULL;
zend_fcall_info_cache fcc; uint32_t param_count = 0;
zval *args; bool status;
int arg_count = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "f*", &fci, &fcc, &args, &arg_count) == FAILURE) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "f*", &entry.fci, &entry.fci_cache, &params, &param_count) == FAILURE) {
RETURN_THROWS(); RETURN_THROWS();
} }
ZVAL_COPY(&entry.function_name, &fci.function_name); Z_TRY_ADDREF(entry.fci.function_name);
entry.arguments = (zval *) safe_emalloc(sizeof(zval), arg_count, 0); zend_fcall_info_argp(&entry.fci, param_count, params);
entry.arg_count = arg_count;
for (int i = 0; i < arg_count; i++) {
ZVAL_COPY(&entry.arguments[i], &args[i]);
}
append_user_shutdown_function(&entry); status = append_user_shutdown_function(&entry);
ZEND_ASSERT(status);
} }
/* }}} */ /* }}} */

View file

@ -143,9 +143,8 @@ PHPAPI double php_get_nan(void);
PHPAPI double php_get_inf(void); PHPAPI double php_get_inf(void);
typedef struct _php_shutdown_function_entry { typedef struct _php_shutdown_function_entry {
zval function_name; zend_fcall_info fci;
zval *arguments; zend_fcall_info_cache fci_cache;
int arg_count;
} php_shutdown_function_entry; } php_shutdown_function_entry;
PHPAPI extern bool register_user_shutdown_function(const char *function_name, size_t function_len, php_shutdown_function_entry *shutdown_function_entry); PHPAPI extern bool register_user_shutdown_function(const char *function_name, size_t function_len, php_shutdown_function_entry *shutdown_function_entry);

View file

@ -12,7 +12,7 @@ class test {
} }
try { try {
register_shutdown_function(array("test","__call")); var_dump(register_shutdown_function(array("test","__call")));
} catch (TypeError $exception) { } catch (TypeError $exception) {
echo $exception->getMessage() . "\n"; echo $exception->getMessage() . "\n";
} }