Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 59 additions & 70 deletions agent/fw_cakephp.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

#include "php_agent.h"
#include "php_error.h"
#include "php_execute.h"
#include "php_user_instrument.h"
#include "php_wrapper.h"
Expand All @@ -12,6 +13,8 @@
#include "util_logging.h"
#include "util_memory.h"

#define PHP_PACKAGE_NAME "cakephp/cakephp"

nr_framework_classification_t nr_cakephp_special_1(
const char* filename TSRMLS_DC) {
NR_UNUSED_TSRMLS;
Expand Down Expand Up @@ -129,15 +132,11 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_pre20) {
NR_PHP_WRAPPER_END

/*
* For CakePHP 2.0 and on, we do things a little differently as the params
* array doesn't exist in the component any more. Instead we hook the
* Controller's invokeAction method. This gets the request as a parameter
* and we get the action from the params array in that object. The
* controller object ($this) has a name, and that name is used (along
* with the word "Controller" appended which is what the CakePHP code does).
*
* CakePHP 2.x is end-of-life and in maintenance mode (critical bugfixes only).
* As such, functionality added in PHP 7.1+ is not well supported.
* For CakePHP 4.0 and on, we retrieve the current controller object
* and are able to extract the controller name from that. We then
* retrieve the request object from the controller and are able to
* extract the action name from that. We then concatenate the two
* strings to form the transaction name.
*
* txn naming scheme:
* In this case, `nr_txn_set_path` is called after `NR_PHP_WRAPPER_CALL` with
Expand All @@ -147,17 +146,17 @@ NR_PHP_WRAPPER_END
* default way of calling the wrapped function in func_end.
*
*/
NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) {
zval* arg1 = 0;
NR_PHP_WRAPPER(nr_cakephp_name_the_wt_4) {
zval* this_var = 0;
zval* czval = 0;
char* controller = 0;
char* action = 0;
int clen = 0;
int alen = 0;
char* name = 0;
zval* params;
zval* azval;
zval* action_zval = NULL;
zval* request = NULL;
zval action_param;

(void)wraprec;
NR_UNUSED_SPECIALFN;
Expand Down Expand Up @@ -193,37 +192,24 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) {
}
}

arg1 = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);
if (!nr_php_is_zval_valid_object(arg1)) {
NR_PHP_WRAPPER_CALL;
goto end;
}

NR_PHP_WRAPPER_CALL;

params = nr_php_get_zval_object_property(arg1, "params" TSRMLS_CC);
if (0 == params) {
nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no params found in request");
request = nr_php_call(this_var, "getRequest");
if (!nr_php_is_zval_valid_object(request)) {
nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no request found in controller");
goto end;
}

if (IS_ARRAY != Z_TYPE_P(params)) {
nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: request params is not an array");
nr_php_zval_str(&action_param, "action");
action_zval = nr_php_call(request, "getParam", &action_param);
zval_dtor(&action_param);
if (!nr_php_is_zval_valid_string(action_zval)) {
nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no action param found in request");
goto end;
}

azval = nr_php_get_zval_object_property(params, "action" TSRMLS_CC);
if (0 == azval) {
nrl_verbosedebug(NRL_FRAMEWORK, "CakePHP: no params['action'] in request");
} else {
if (!nr_php_is_zval_valid_string(azval)) {
nrl_verbosedebug(NRL_FRAMEWORK,
"CakePHP: no string-valued params['action'] in request");
} else {
alen = Z_STRLEN_P(azval);
action = (char*)nr_alloca(alen + 1);
nr_strxcpy(action, Z_STRVAL_P(azval), alen);
}
alen = Z_STRLEN_P(action_zval);
action = (char*)nr_alloca(alen + 1);
nr_strxcpy(action, Z_STRVAL_P(action_zval), alen);
}

if ((0 == clen) && (0 == alen)) {
Expand All @@ -249,8 +235,9 @@ NR_PHP_WRAPPER(nr_cakephp_name_the_wt_2) {
NR_NOT_OK_TO_OVERWRITE);

end:
nr_php_arg_release(&arg1);
nr_php_scope_release(&this_var);
nr_php_zval_free(&request);
nr_php_zval_free(&action_zval);
}
NR_PHP_WRAPPER_END

Expand Down Expand Up @@ -281,35 +268,38 @@ NR_PHP_WRAPPER(nr_cakephp_problem_1) {
NR_PHP_WRAPPER_END

/*
* CakePHP 2.0+
* CakePHP 4.0+
*
* If the action or controller is not found during the dispatch process, the
* appropriate Exception will be created and thrown. We wrap the CakeException
* constructor instead of the Exception handler, since CakePHP allows for the
* handler to be completely replaced.
*
* CakePHP 2.x is end-of-life and in maintenance mode (critical bugfixes only).
* As such, functionality added in PHP 7.1+ is not well supported.
*
* txn naming scheme:
* In this case, `nr_txn_set_path` is called before `NR_PHP_WRAPPER_CALL` with
* `NR_NOT_OK_TO_OVERWRITE` and as this corresponds to calling the wrapped
* function in func_begin it needs to be explicitly set as a before_callback to
* ensure OAPI compatibility. This entails that the first wrapped call gets to
* name the txn.
* Report errors and exceptions caught by CakePHP's error handler.
*/
NR_PHP_WRAPPER(nr_cakephp_problem_2) {
const char* name = "Exception";
NR_PHP_WRAPPER(nr_cakephp_error_handler_wrapper) {
zval* exception = NULL;

(void)wraprec;
NR_UNUSED_SPECIALFN;
(void)wraprec;

NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_CAKEPHP);

nr_txn_set_path("CakePHP", NRPRG(txn), name, NR_PATH_TYPE_ACTION,
exception = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS);
if (!nr_php_is_zval_valid_object(exception)) {
nrl_verbosedebug(NRL_FRAMEWORK, "%s: exception is NULL or not an object",
__func__);
goto end;
}

if (NR_SUCCESS
!= nr_php_error_record_exception(
NRPRG(txn), exception, nr_php_error_get_priority(E_ERROR), true,
"Uncaught exception ", &NRPRG(exception_filters))) {
nrl_verbosedebug(NRL_FRAMEWORK, "%s: unable to record exception", __func__);
}

nr_txn_set_path("CakePHP", NRPRG(txn),
nr_php_get_server_global("REQUEST_URI"), NR_PATH_TYPE_ACTION,
NR_NOT_OK_TO_OVERWRITE);

NR_PHP_WRAPPER_CALL;
end:
nr_php_arg_release(&exception);
}
NR_PHP_WRAPPER_END

Expand All @@ -324,17 +314,16 @@ void nr_cakephp_enable_1(TSRMLS_D) {
}

/*
* Enable CakePHP 2.0+
* Enable CakePHP 4.0+
*/
void nr_cakephp_enable_2(TSRMLS_D) {
nr_php_wrap_user_function(NR_PSTR("Controller::invokeAction"),
nr_cakephp_name_the_wt_2 TSRMLS_CC);
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
nr_php_wrap_user_function_before_after_clean(
NR_PSTR("CakeException::__construct"), nr_cakephp_problem_2, NULL, NULL);
#else
nr_php_wrap_user_function(NR_PSTR("CakeException::__construct"),
nr_cakephp_problem_2 TSRMLS_CC);
#endif
void nr_cakephp_enable(TSRMLS_D) {
nr_php_wrap_user_function(
NR_PSTR("Cake\\Controller\\Controller::invokeAction"),
nr_cakephp_name_the_wt_4);
nr_php_wrap_user_function(
NR_PSTR(
"Cake\\Error\\Middleware\\ErrorHandlerMiddleware::handleException"),
nr_cakephp_error_handler_wrapper);
nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
PHP_PACKAGE_VERSION_UNKNOWN);
}
2 changes: 1 addition & 1 deletion agent/fw_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#include "php_execute.h"

extern void nr_cakephp_enable_1(TSRMLS_D);
extern void nr_cakephp_enable_2(TSRMLS_D);
extern void nr_cakephp_enable(TSRMLS_D);
extern nr_framework_classification_t nr_cakephp_special_1(
const char* filename TSRMLS_DC);
extern nr_framework_classification_t nr_cakephp_special_2(
Expand Down
13 changes: 2 additions & 11 deletions agent/php_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,8 @@ typedef struct _nr_framework_table_t {
*/
// clang-format: off
static const nr_framework_table_t all_frameworks[] = {
/*
* Watch out:
* cake1.2 and cake1.3 use a subdirectory named 'cake' (lower case)
* cake2.0 and on use a subdirectory named 'Cake' (upper case file name)
*/
{"CakePHP", "cakephp", NR_PSTR("cake/libs/object.php"), nr_cakephp_special_1,
nr_cakephp_enable_1, NR_FW_CAKEPHP},
{"CakePHP", "cakephp", NR_PSTR("cake/core/app.php"), nr_cakephp_special_2,
nr_cakephp_enable_2, NR_FW_CAKEPHP},
{"CakePHP", "cakephp", NR_PSTR("cakephp/src/core/functions.php"), 0,
nr_cakephp_enable, NR_FW_CAKEPHP},

/*
* Watch out: frameworks or CMS' build on top of CodeIgniter might not get
Expand Down Expand Up @@ -522,8 +515,6 @@ static nr_library_table_t libraries[] = {
* with other frameworks or even without a framework at all.
*/
{"Laminas_Http", NR_PSTR("laminas-http/src/client.php"), nr_laminas_http_enable},

{"CakePHP3", NR_PSTR("cakephp/src/core/functions.php"), NULL},
};
// clang-format: on

Expand Down