diff --git a/Makefile b/Makefile index 0d258f928..a5f5ca1e9 100644 --- a/Makefile +++ b/Makefile @@ -314,7 +314,7 @@ src/newrelic/infinite_tracing/com_newrelic_trace_v1/v1.pb.go: protocol/infinite_ .PHONY: integration integration: Makefile daemon lasp-test-all - for PHP in $${PHPS:-7.4 7.3 7.2 7.1 7.0 5.6 5.5 5.4 5.3}; do \ + for PHP in $${PHPS:-8.0 7.4 7.3 7.2 7.1 7.0 5.6 5.5 5.4 5.3}; do \ echo; echo "# PHP=$${PHP}"; \ env NRLAMP_PHP=$${PHP} bin/integration_runner $(INTEGRATION_ARGS) || exit 1; \ echo "# PHP=$${PHP}"; \ @@ -379,7 +379,7 @@ lasp-test: daemon if [ ! $(SUITE_LASP) ]; then echo "USAGE: make lasp-test SUITE_LASP=suite-most-secure"; exit 1; fi if [ "$(LICENSE_lasp_$(subst -,_,$(SUITE_LASP)))" = "" ] ; then echo "Missing license for $(SUITE_LASP)"; exit 1; fi if [ ! -d "tests/lasp/$(SUITE_LASP)" ]; then echo "No such suite in tests/lasp folder"; exit 1; fi - for PHP in $${PHPS:-7.4 7.3 7.2 7.1 7.0 5.6 5.5 5.4 5.3}; do \ + for PHP in $${PHPS:-8.0 7.4 7.3 7.2 7.1 7.0 5.6 5.5 5.4 5.3}; do \ echo; echo "# PHP=$${PHP}"; \ NRLAMP_PHP=$${PHP} bin/integration_runner $(INTEGRATION_ARGS) -loglevel debug \ -license $(LICENSE_lasp_$(subst -,_,$(SUITE_LASP))) \ diff --git a/VERSION b/VERSION index 48f3ef687..2ac37e210 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.17.0 +9.17.1 diff --git a/agent/php_internal_instrument.c b/agent/php_internal_instrument.c index e9765a692..c7bb09186 100644 --- a/agent/php_internal_instrument.c +++ b/agent/php_internal_instrument.c @@ -749,6 +749,39 @@ NR_INNER_WRAPPER(mysqli_options) { } } +static void nr_php_instrument_datastore_operation_call( + const nrinternalfn_t* nr_wrapper, + nr_datastore_t datastore, + const char* operation, + nr_datastore_instance_t* instance, + INTERNAL_FUNCTION_PARAMETERS) { + int zcaught = 0; + nr_segment_t* segment = NULL; + nr_segment_datastore_params_t params = { + .datastore = { + .type = datastore, + }, + .operation = nr_strdup(operation), + .instance = instance, + .callbacks = { + .backtrace = nr_php_backtrace_callback, + }, + }; + + segment = nr_segment_start(NRPRG(txn), NULL, NULL); + zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, + INTERNAL_FUNCTION_PARAM_PASSTHRU); + + nr_segment_datastore_end(&segment, ¶ms); + + nr_free(params.operation); + + if (zcaught) { + zend_bailout(); + /* NOTREACHED */ + } +} + /* * Handle * bool mysqli_commit ( object $link [, int $flags=0, string $name] ) @@ -756,15 +789,10 @@ NR_INNER_WRAPPER(mysqli_options) { */ NR_INNER_WRAPPER(mysqli_commit) { zval* mysqli_obj = NULL; - nr_segment_t* segment = NULL; nr_datastore_instance_t* instance = NULL; - nr_explain_plan_t* plan = NULL; char* name = NULL; - char* sqlstr = NULL; - nr_string_len_t sqlstrlen; zend_long flags = 0; nr_string_len_t name_len = 0; - int zcaught = 0; if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, @@ -780,35 +808,9 @@ NR_INNER_WRAPPER(mysqli_commit) { mysqli_obj = NR_PHP_INTERNAL_FN_THIS(); } } - - if (NULL != name) { - sqlstr = nr_formatf("%s %s", "COMMIT", name); - } else { - sqlstr = nr_formatf("%s", "COMMIT"); - } - sqlstrlen = nr_strlen(sqlstr); - - segment = nr_segment_start(NRPRG(txn), NULL, NULL); - - if (nrlikely(NULL != segment)) { - /* - * Set the stop time now so that we don't include the explain plan time. - */ - segment->stop_time = nr_txn_now_rel(NRPRG(txn)); - instance = nr_php_mysqli_retrieve_datastore_instance(mysqli_obj TSRMLS_CC); - - nr_php_txn_end_segment_sql(&segment, sqlstr, sqlstrlen, plan, - NR_DATASTORE_MYSQL, instance TSRMLS_CC); - - nr_explain_plan_destroy(&plan); - - } - - nr_free(sqlstr); - if (zcaught) { - zend_bailout(); - /* NOTREACHED */ - } + nr_php_instrument_datastore_operation_call(nr_wrapper, NR_DATASTORE_MYSQL, + "commit", instance, + INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* @@ -1445,39 +1447,6 @@ NR_INNER_WRAPPER(mysqli_stmt_prepare) { } } -static void nr_php_instrument_datastore_operation_call( - const nrinternalfn_t* nr_wrapper, - nr_datastore_t datastore, - const char* operation, - nr_datastore_instance_t* instance, - INTERNAL_FUNCTION_PARAMETERS) { - int zcaught = 0; - nr_segment_t* segment = NULL; - nr_segment_datastore_params_t params = { - .datastore = { - .type = datastore, - }, - .operation = nr_strdup(operation), - .instance = instance, - .callbacks = { - .backtrace = nr_php_backtrace_callback, - }, - }; - - segment = nr_segment_start(NRPRG(txn), NULL, NULL); - zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler, - INTERNAL_FUNCTION_PARAM_PASSTHRU); - - nr_segment_datastore_end(&segment, ¶ms); - - nr_free(params.operation); - - if (zcaught) { - zend_bailout(); - /* NOTREACHED */ - } -} - /* * Handle * bool memcache_xxx( ... ) @@ -2698,11 +2667,13 @@ NR_INNER_WRAPPER(curl_multi_exec) { int rv = FAILURE; #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP 8.0+ */ - rv = zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, - "ol", &curlres, &still_running); + rv = zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "ol", &curlres, + &still_running); #else - rv = zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, - "rl", &curlres, &still_running); + rv = zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, + ZEND_NUM_ARGS() TSRMLS_CC, "rl", &curlres, + &still_running); #endif /* PHP 8.0+ */ if (SUCCESS != rv) { diff --git a/agent/php_pdo.c b/agent/php_pdo.c index a97f1ed3d..a34a8e839 100644 --- a/agent/php_pdo.c +++ b/agent/php_pdo.c @@ -130,11 +130,11 @@ int nr_php_pdo_rebind_apply_parameter(struct pdo_bound_param_data* param, zval* type = nr_php_zval_alloc(); zval* retval = NULL; -#ifdef PHP7 +#if ZEND_MODULE_API_NO >= ZEND_7_0_X_API_NO /* PHP 7.0+ */ value = ¶m->parameter; #else value = param->parameter; -#endif /* PHP7 */ +#endif /* PHP 7.0+ */ if (nr_php_zend_hash_key_is_string(hash_key)) { /* @@ -152,6 +152,16 @@ int nr_php_pdo_rebind_apply_parameter(struct pdo_bound_param_data* param, ZVAL_LONG(type, param->param_type); +#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP 8.0+ */ + /* + * Create a reference to value to prevent PHP 8 from + * emitting a warning. + */ + if (!Z_ISREF_P(value)) { + Z_TRY_ADDREF_P(value); + ZVAL_NEW_REF(value, value); + } +#endif retval = nr_php_call(stmt, "bindParam", key, value, type); nr_php_zval_free(&key); diff --git a/axiom/nr_version.c b/axiom/nr_version.c index 9cb20f53b..ac71ced95 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -25,10 +25,10 @@ * Here's a list: * https://en.wikipedia.org/wiki/List_of_colors_(compact) * - * ube (9.14) - * vanilla (9.15) - * watermelon (9.16) - * xigua (9.17) + * ube 29Oct2020 (9.14) + * vanilla 07Dec2020 (9.15) + * watermelon 25Jan2021 (9.16) + * xigua 21Apr2021 (9.17) */ #define NR_CODENAME "xigua" diff --git a/docs/development.md b/docs/development.md index 28466a442..efc84b9d1 100644 --- a/docs/development.md +++ b/docs/development.md @@ -59,7 +59,7 @@ _(most operating systems package these with `-dev` or `-devel` suffixes)_ ### PHP -The PHP agent supports PHP versions `5.3`, `5.4`, `5.5`, `5.6`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, and `8.0`. +The PHP agent supports PHP versions `5.5`, `5.6`, `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, and `8.0`. ## Build the PHP Agent diff --git a/tests/integration/pdo/test_prepared_stmt_params.php b/tests/integration/pdo/test_prepared_stmt_params.php new file mode 100644 index 000000000..c5be3be40 --- /dev/null +++ b/tests/integration/pdo/test_prepared_stmt_params.php @@ -0,0 +1,93 @@ +", + "?? SQL id", + "select * from tables where table_name = ? limit ?;", + "Datastore/statement/MySQL/tables/select", + 1, + "?? total time", + "?? min time", + "?? max time", + { + "backtrace": [ + " in PDOStatement::execute called at __FILE__ (??)", + " in test_prepared_statement called at __FILE__ (??)" + ], + "explain_plan": [ + [ + "id", + "select_type", + "table", + "type", + "possible_keys", + "key", + "key_len", + "ref", + "rows", + "Extra" + ], + [ + [ + "1", + "SIMPLE", + "tables", + "ALL", + null, + "TABLE_NAME", + null, + null, + "??", + "??" + ] + ] + ] + } + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/tap.php'); +require_once(realpath (dirname ( __FILE__ )) . '/pdo.inc'); + +function test_prepared_statement() { + global $PDO_MYSQL_DSN, $MYSQL_USER, $MYSQL_PASSWD; + + $conn = new PDO($PDO_MYSQL_DSN, $MYSQL_USER, $MYSQL_PASSWD); + $stmt = $conn->prepare('select * from tables where table_name = ? limit 1;'); + $stmt->bindValue(1, "missing"); + tap_assert($stmt->execute(), 'execute prepared statement with a param'); +} + +test_prepared_statement();