Skip to content

Commit

Permalink
fix(agent): Resolve mysqli deprecation warnings on PHP 8.1+ (#750)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bduranleau-nr authored Nov 1, 2023
1 parent 9db916e commit 51759a5
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 6 deletions.
73 changes: 67 additions & 6 deletions agent/php_internal_instrument.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -794,20 +808,29 @@ 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;
} else {
mysqli_obj = NR_PHP_INTERNAL_FN_THIS();
}
}

nr_php_instrument_datastore_operation_call(nr_wrapper, NR_DATASTORE_MYSQL,
"commit", instance,
INTERNAL_FUNCTION_PARAM_PASSTHRU);
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
36 changes: 36 additions & 0 deletions tests/integration/mysqli/test_null_connect_proc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
The agent should not cause a deprecation warning to be thrown when passing null
to optional arguments for mysqli_connect on PHP 8.1+
*/

/*SKIPIF
<?php require("skipif.inc");
if (version_compare(PHP_VERSION, "8.1", "<")) {
die("skip: Deprecation warning PHP 8.1+ specific\n");
}
*/

/*INI
error_reporting = E_DEPRECATION
*/

/*EXPECT
*/

require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php');

$link = mysqli_connect(null, null, null, null, null, null);

if (mysqli_connect_errno()) {
echo mysqli_connect_error() . "\n";
exit(1);
}

mysqli_close($link);
36 changes: 36 additions & 0 deletions tests/integration/mysqli/test_null_construct_oo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
The agent should not cause a deprecation warning to be thrown when passing null
to optional arguments for mysqli::__construct on PHP 8.1+
*/

/*SKIPIF
<?php require("skipif.inc");
if (version_compare(PHP_VERSION, "8.1", "<")) {
die("skip: Deprecation warning PHP 8.1+ specific\n");
}
*/

/*INI
error_reporting = E_DEPRECATION
*/

/*EXPECT
*/

require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php');

$link = new mysqli(null, null, null, null, null, null);

if (mysqli_connect_errno()) {
echo mysqli_connect_error() . "\n";
exit(1);
}

mysqli_close($link);
38 changes: 38 additions & 0 deletions tests/integration/mysqli/test_null_rc_oo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
The agent should not cause a deprecation warning to be thrown when passing null
to optional arguments for mysqli::real_connect on PHP 8.1+
*/

/*SKIPIF
<?php require("skipif.inc");
if (version_compare(PHP_VERSION, "8.1", "<")) {
die("skip: Deprecation warning PHP 8.1+ specific\n");
}
*/

/*INI
error_reporting = E_DEPRECATION
*/

/*EXPECT
*/

require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php');

$link = mysqli_init();
$link->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);
39 changes: 39 additions & 0 deletions tests/integration/mysqli/test_null_rc_proc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
The agent should not cause a deprecation warning to be thrown when passing null
to optional arguments for mysqli_real_connect on PHP 8.1+
*/

/*SKIPIF
<?php require("skipif.inc");
if (version_compare(PHP_VERSION, "8.1", "<")) {
die("skip: Deprecation warning PHP 8.1+ specific\n");
}
*/

/*INI
error_reporting = E_DEPRECATION
*/

/*EXPECT
*/

require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php');

$link = mysqli_init();
mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, 10);

mysqli_real_connect($link, null, null, null, null, null, null);

if (mysqli_connect_errno()) {
echo mysqli_connect_error() . "\n";
exit(1);
}

mysqli_close($link);
60 changes: 60 additions & 0 deletions tests/integration/mysqli/test_null_stmt_construct.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
The agent should not cause a deprecation warning to be thrown when passing null
to optional arguments for mysqli_stmt:__construct on PHP 8.1+
*/

/*SKIPIF
<?php require("skipif.inc");
if (version_compare(PHP_VERSION, "8.1", "<")) {
die("skip: Deprecation warning PHP 8.1+ specific\n");
}
*/

/*INI
error_reporting = E_ALL
*/

/*EXPECT
STATISTICS
*/

require_once(realpath (dirname ( __FILE__ )) . '/../../include/config.php');

function test_stmt_prepare($link)
{
$stmt = new mysqli_stmt($link, null);
$name = 'STATISTICS';

$query = "SELECT TABLE_NAME FROM information_schema.tables WHERE table_name=?";
if (FALSE === $stmt->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);

0 comments on commit 51759a5

Please sign in to comment.