From 51759a507e7b33dcf507d4460e9f99814e706c12 Mon Sep 17 00:00:00 2001 From: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Date: Wed, 1 Nov 2023 09:27:43 -0500 Subject: [PATCH] fix(agent): Resolve mysqli deprecation warnings on PHP 8.1+ (#750) Certain wrapped mysqli functions allow null values to be passed for optional values on PHP 8.1+. This PR fixes the issue of the agent not properly expecting these null values in zpp resulting in deprecation warnings being thrown. --- agent/php_internal_instrument.c | 73 +++++++++++++++++-- .../mysqli/test_null_connect_proc.php | 36 +++++++++ .../mysqli/test_null_construct_oo.php | 36 +++++++++ tests/integration/mysqli/test_null_rc_oo.php | 38 ++++++++++ .../integration/mysqli/test_null_rc_proc.php | 39 ++++++++++ .../mysqli/test_null_stmt_construct.php | 60 +++++++++++++++ 6 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 tests/integration/mysqli/test_null_connect_proc.php create mode 100644 tests/integration/mysqli/test_null_construct_oo.php create mode 100644 tests/integration/mysqli/test_null_rc_oo.php create mode 100644 tests/integration/mysqli/test_null_rc_proc.php create mode 100644 tests/integration/mysqli/test_null_stmt_construct.php diff --git a/agent/php_internal_instrument.c b/agent/php_internal_instrument.c index a2c898ff5..a314de027 100644 --- a/agent/php_internal_instrument.c +++ b/agent/php_internal_instrument.c @@ -643,14 +643,28 @@ NR_INNER_WRAPPER(mysqli_construct) { zval* mysqli_obj = NULL; int zcaught = 0; +#if ZEND_MODULE_API_NO >= ZEND_8_1_X_API_NO + bool port_is_null = 1; + const char *type_spec = "|s!s!s!s!l!s!"; if (FAILURE == zend_parse_parameters_ex( - ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|ssssls", &host, + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, type_spec, &host, + &host_len, &username, &username_len, &password, &password_len, + &database, &database_len, &port, &port_is_null, &socket, &socket_len)) { + nr_wrapper->oldhandler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; + } +#else + const char *type_spec = "|ssssls"; + if (FAILURE + == zend_parse_parameters_ex( + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, type_spec, &host, &host_len, &username, &username_len, &password, &password_len, &database, &database_len, &port, &socket, &socket_len)) { nr_wrapper->oldhandler(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; } +#endif zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, INTERNAL_FUNCTION_PARAM_PASSTHRU); @@ -794,13 +808,21 @@ NR_INNER_WRAPPER(mysqli_commit) { zend_long flags = 0; nr_string_len_t name_len = 0; +#if ZEND_MODULE_API_NO >= ZEND_8_1_X_API_NO + const char *proc_type_spec = "o|ls!"; + const char *oo_type_spec = "|ls!"; +#else + const char *proc_type_spec = "o|ls"; + const char *oo_type_spec = "|ls"; +#endif + if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, - ZEND_NUM_ARGS() TSRMLS_CC, "o|ls", + ZEND_NUM_ARGS() TSRMLS_CC, proc_type_spec, &mysqli_obj, &flags, &name, &name_len)) { if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, - ZEND_NUM_ARGS() TSRMLS_CC, "|ls", &flags, + ZEND_NUM_ARGS() TSRMLS_CC, oo_type_spec, &flags, &name, &name_len)) { nr_wrapper->oldhandler(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; @@ -808,6 +830,7 @@ NR_INNER_WRAPPER(mysqli_commit) { mysqli_obj = NR_PHP_INTERNAL_FN_THIS(); } } + nr_php_instrument_datastore_operation_call(nr_wrapper, NR_DATASTORE_MYSQL, "commit", instance, INTERNAL_FUNCTION_PARAM_PASSTHRU); @@ -849,15 +872,47 @@ NR_INNER_WRAPPER(mysqli_real_connect) { zval* mysqli_obj = NULL; int zcaught = 0; + /* PHP 8.1 and later will report a deprecation warning if null is sent where + * a non-null argument value is expected. For these PHP versions we use the + * same argument type specification as the mysqli::real_connect() extension + * uses to avoid creating this deprecation warning. + * For older PHPs continue to use the same specification string as previously + * to minimize any chances of introducing new problems. + */ +#if ZEND_MODULE_API_NO >= ZEND_8_1_X_API_NO + bool port_is_null = 1; + const char *proc_type_spec = "o|s!s!s!s!l!s!l"; + const char *oo_type_spec = "|s!s!s!s!l!s!l"; if (FAILURE == zend_parse_parameters_ex( - ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "o|sssslsl", + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, proc_type_spec, + &mysqli_obj, &host, &host_len, &username, &username_len, &password, + &password_len, &database, &database_len, &port, &port_is_null, &socket, &socket_len, + &flags)) { + if (FAILURE + == zend_parse_parameters_ex( + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, oo_type_spec, + &host, &host_len, &username, &username_len, &password, + &password_len, &database, &database_len, &port, &port_is_null, &socket, + &socket_len, &flags)) { + nr_wrapper->oldhandler(INTERNAL_FUNCTION_PARAM_PASSTHRU); + return; + } else { + mysqli_obj = NR_PHP_INTERNAL_FN_THIS(); + } + } +#else + const char *proc_type_spec = "o|sssslsl"; + const char *oo_type_spec = "|sssslsl"; + if (FAILURE + == zend_parse_parameters_ex( + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, proc_type_spec, &mysqli_obj, &host, &host_len, &username, &username_len, &password, &password_len, &database, &database_len, &port, &socket, &socket_len, &flags)) { if (FAILURE == zend_parse_parameters_ex( - ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|sssslsl", + ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, oo_type_spec, &host, &host_len, &username, &username_len, &password, &password_len, &database, &database_len, &port, &socket, &socket_len, &flags)) { @@ -867,6 +922,7 @@ NR_INNER_WRAPPER(mysqli_real_connect) { mysqli_obj = NR_PHP_INTERNAL_FN_THIS(); } } +#endif zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, INTERNAL_FUNCTION_PARAM_PASSTHRU); @@ -1339,9 +1395,14 @@ NR_INNER_WRAPPER(mysqli_stmt_construct) { char* sqlstr = NULL; nr_string_len_t sqlstrlen = 0; +#if ZEND_MODULE_API_NO >= ZEND_8_1_X_API_NO + const char *type_spec = "o|s!"; +#else + const char *type_spec = "o|s"; +#endif if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, - ZEND_NUM_ARGS() TSRMLS_CC, "o|s", &mysqli_obj, + ZEND_NUM_ARGS() TSRMLS_CC, type_spec, &mysqli_obj, &sqlstr, &sqlstrlen)) { nr_wrapper->oldhandler(INTERNAL_FUNCTION_PARAM_PASSTHRU); return; diff --git a/tests/integration/mysqli/test_null_connect_proc.php b/tests/integration/mysqli/test_null_connect_proc.php new file mode 100644 index 000000000..4981f3284 --- /dev/null +++ b/tests/integration/mysqli/test_null_connect_proc.php @@ -0,0 +1,36 @@ +options(MYSQLI_OPT_CONNECT_TIMEOUT, 10); +$link->real_connect(null, null, null, null, null, null); + +if (mysqli_connect_errno()) { + echo mysqli_connect_error() . "\n"; + exit(1); +} + +mysqli_close($link); diff --git a/tests/integration/mysqli/test_null_rc_proc.php b/tests/integration/mysqli/test_null_rc_proc.php new file mode 100644 index 000000000..74766ab53 --- /dev/null +++ b/tests/integration/mysqli/test_null_rc_proc.php @@ -0,0 +1,39 @@ +prepare($query) || + FALSE === $stmt->bind_param('s', $name) || + FALSE === $stmt->execute() || + FALSE === $stmt->bind_result($name)) { + echo mysqli_stmt_error($stmt) . "\n"; + mysqli_stmt_close($stmt); + return; + } + + while (mysqli_stmt_fetch($stmt)) { + echo $name . "\n"; + } + + mysqli_stmt_close($stmt); +} + +$link = mysqli_connect($MYSQL_HOST, $MYSQL_USER, $MYSQL_PASSWD, $MYSQL_DB, $MYSQL_PORT, $MYSQL_SOCKET); +if (mysqli_connect_errno()) { + echo mysqli_connect_error() . "\n"; + exit(1); +} + +test_stmt_prepare($link); +mysqli_close($link); +