Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Laravel Lumen support #38

Merged
merged 10 commits into from
Dec 2, 2020
2 changes: 1 addition & 1 deletion agent/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ if test "$PHP_NEWRELIC" = "yes"; then

dnl Actually define our extension, and far more importantly, its source
dnl files.
PHP_NEW_EXTENSION(newrelic, fw_cakephp.c fw_codeigniter.c fw_drupal8.c fw_drupal.c fw_drupal_common.c fw_joomla.c fw_kohana.c fw_laminas3.c fw_laravel.c fw_laravel_queue.c fw_magento1.c fw_magento2.c fw_magento_common.c fw_mediawiki.c fw_silex.c fw_slim.c fw_support.c fw_symfony4.c fw_symfony2.c fw_symfony.c fw_symfony_common.c fw_wordpress.c fw_yii.c fw_zend2.c fw_zend.c lib_doctrine2.c lib_guzzle3.c lib_guzzle4.c lib_guzzle6.c lib_guzzle_common.c lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c php_agent.c php_api.c php_api_datastore.c php_api_distributed_trace.c php_api_internal.c php_autorum.c php_call.c php_curl.c php_curl_md.c php_datastore.c php_environment.c php_error.c php_execute.c php_explain.c php_explain_mysqli.c php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c php_globals.c php_hash.c php_header.c php_httprequest_send.c php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c php_mysqli.c php_newrelic.c php_nrini.c php_output.c php_pdo.c php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c php_rinit.c php_rshutdown.c php_samplers.c php_stack.c php_stacked_segment.c php_txn.c php_user_instrument.c php_vm.c php_wrapper.c, $ext_shared,, \\$(NEWRELIC_CFLAGS))
PHP_NEW_EXTENSION(newrelic, fw_cakephp.c fw_codeigniter.c fw_drupal8.c fw_drupal.c fw_drupal_common.c fw_joomla.c fw_kohana.c fw_laminas3.c fw_laravel.c fw_laravel_queue.c fw_lumen.c fw_magento1.c fw_magento2.c fw_magento_common.c fw_mediawiki.c fw_silex.c fw_slim.c fw_support.c fw_symfony4.c fw_symfony2.c fw_symfony.c fw_symfony_common.c fw_wordpress.c fw_yii.c fw_zend2.c fw_zend.c lib_doctrine2.c lib_guzzle3.c lib_guzzle4.c lib_guzzle6.c lib_guzzle_common.c lib_mongodb.c lib_phpunit.c lib_predis.c lib_zend_http.c php_agent.c php_api.c php_api_datastore.c php_api_distributed_trace.c php_api_internal.c php_autorum.c php_call.c php_curl.c php_curl_md.c php_datastore.c php_environment.c php_error.c php_execute.c php_explain.c php_explain_mysqli.c php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c php_globals.c php_hash.c php_header.c php_httprequest_send.c php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c php_mysqli.c php_newrelic.c php_nrini.c php_output.c php_pdo.c php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c php_rinit.c php_rshutdown.c php_samplers.c php_stack.c php_stacked_segment.c php_txn.c php_user_instrument.c php_vm.c php_wrapper.c, $ext_shared,, \\$(NEWRELIC_CFLAGS))

PHP_SUBST(NEWRELIC_CFLAGS)

Expand Down
1 change: 1 addition & 0 deletions agent/fw_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern void nr_joomla_enable(TSRMLS_D);
extern void nr_kohana_enable(TSRMLS_D);
extern void nr_laminas3_enable(TSRMLS_D);
extern void nr_laravel_enable(TSRMLS_D);
extern void nr_lumen_enable(TSRMLS_D);
extern void nr_magento1_enable(TSRMLS_D);
extern void nr_magento2_enable(TSRMLS_D);
extern void nr_mediawiki_enable(TSRMLS_D);
Expand Down
209 changes: 209 additions & 0 deletions agent/fw_lumen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "php_agent.h"
#include "php_call.h"
#include "php_error.h"
#include "php_user_instrument.h"
#include "php_execute.h"
#include "php_wrapper.h"
#include "php_hash.h"
#include "fw_hooks.h"
#include "util_logging.h"
#include "util_memory.h"
#include "util_strings.h"

/*
* Sets the web transaction name. If strip_base == true,
* leading class path components will be stripped.
*/
static int nr_lumen_name_the_wt(const char* name TSRMLS_DC,
const char* lumen_version,
bool strip_base) {
const char* path = NULL;

if (NULL == name) {
return NR_FAILURE;
}

if (strip_base) {
path = strrchr(name, '\\');
// Backslash was not found
if (NULL == path) {
path = name;
} else {
path += 1;
}
}

nr_txn_set_path(
lumen_version, NRPRG(txn), path, NR_PATH_TYPE_ACTION,
NR_OK_TO_OVERWRITE); /* Watch out: this name is OK to overwrite */

return NR_SUCCESS;
}

/*
* Wrapper around nr_lumen_name_the_wt for zval strings
*/
static int nr_lumen_name_the_wt_from_zval(const zval* name TSRMLS_DC,
const char* lumen_version,
bool strip_base) {
int rc = NR_FAILURE;
if (nrlikely(nr_php_is_zval_non_empty_string(name))) {
char* name_str = nr_strndup(Z_STRVAL_P(name), Z_STRLEN_P(name));
rc = nr_lumen_name_the_wt(name_str TSRMLS_CC, lumen_version, strip_base);
nr_free(name_str);
}

return rc;
}

/*
* Core transaction naming logic. Wraps the function that correlates
* requests to routes
*/
NR_PHP_WRAPPER(nr_lumen_handle_found_route) {
zval* route_info = NULL;

/* Warning avoidance */
(void)wraprec;

/* Verify that we are using Lumen, otherwise bail. */
NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_LUMEN);

/* $routeInfo object used by Application */
route_info = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);

/* We expect route_info to be an array. At index 1, if we see an
* 'as' key, then we have access to the route, otherwise, if we have
* a 'uses' key we have access to the controller and action.
* See: https://lumen.laravel.com/docs/8.x/routing#named-routes
*/
if (!nr_php_is_zval_valid_array(route_info)) {
nrl_verbosedebug(NRL_TXN, "Lumen: $routeInfo was not an array");
NR_PHP_WRAPPER_CALL;
goto end;
}

NR_PHP_WRAPPER_CALL;

/* obtain $routeInfo[1] */
zend_ulong idx = 1;
zval* route_info_pos
= nr_php_zend_hash_index_find(Z_ARRVAL_P(route_info), idx);

/* obtain $routeInfo[1]['as'] for route name */
zval* route_name = NULL;
if (NULL != route_info_pos) {
route_name = nr_php_zend_hash_find(Z_ARRVAL_P(route_info_pos), "as");
}

if (NULL != route_name) {
if (NR_SUCCESS
!= nr_lumen_name_the_wt_from_zval(route_name TSRMLS_CC, "Lumen", 0)) {
nrl_verbosedebug(NRL_TXN, "Lumen: located route name is a non-string");
}
} else {
/* No route located, use controller instead */
nrl_verbosedebug(
NRL_TXN,
"Lumen: unable locate route, attempting to use controller instead");

/* obtain $routeInfo[1]['uses'] for controller name */
zval* controller_name
= nr_php_zend_hash_find(Z_ARRVAL_P(route_info_pos), "uses");

if (NULL != controller_name) {
if (NR_SUCCESS
!= nr_lumen_name_the_wt_from_zval(controller_name TSRMLS_CC, "Lumen",
1)) {
nrl_verbosedebug(NRL_TXN,
"Lumen: located controller name is a non-string");
}

} else {
nrl_verbosedebug(NRL_TXN, "Lumen: unable to locate controller or route");
}
}

end:
nr_php_arg_release(&route_info);
}
NR_PHP_WRAPPER_END

/*
* Exception handling logic. Wraps the function that routes
* exceptions to their respective handlers
*/
NR_PHP_WRAPPER(nr_lumen_exception) {
zval* exception = NULL;

NR_UNUSED_SPECIALFN;
(void)wraprec;

NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_LUMEN);

#if ZEND_MODULE_API_NO >= ZEND_5_4_X_API_NO
const char* class_name = NULL;
const char* ignored = NULL;
#else
char* class_name = NULL;
char* ignored = NULL;
#endif /* PHP >= 5.4 */

char* name = NULL;

/*
* When the exception handler renders the response, name the transaction
* after the exception handler using the same format used for controller
* actions. e.g. Controller@action.
*/
class_name = get_active_class_name(&ignored TSRMLS_CC);
name = nr_formatf("%s@%s", class_name, get_active_function_name(TSRMLS_C));
nr_lumen_name_the_wt(name TSRMLS_CC, "Lumen", 1);
nr_free(name);

exception = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS TSRMLS_CC);
if (NULL == exception) {
nrl_verbosedebug(NRL_FRAMEWORK, "%s: $e is NULL", __func__);
NR_PHP_WRAPPER_CALL;
goto end;
}

NR_PHP_WRAPPER_CALL;

nr_status_t st;
int priority = nr_php_error_get_priority(E_ERROR);

st = nr_php_error_record_exception(NRPRG(txn), exception, priority,
NULL /* use default prefix */,
&NRPRG(exception_filters) TSRMLS_CC);

if (NR_FAILURE == st) {
nrl_verbosedebug(NRL_FRAMEWORK, "%s: unable to record exception", __func__);
}

end:
nr_php_arg_release(&exception);
}
NR_PHP_WRAPPER_END

void nr_lumen_enable(TSRMLS_D) {
/*
* We set the path to 'unknown' to prevent having to name routing errors.
* This follows what is done in the symfony logic
*/
nr_txn_set_path("Lumen", NRPRG(txn), "unknown", NR_PATH_TYPE_ACTION,
NR_OK_TO_OVERWRITE);

nr_php_wrap_user_function(
NR_PSTR("Laravel\\Lumen\\Application::handleFoundRoute"),
nr_lumen_handle_found_route TSRMLS_CC);

nr_php_wrap_user_function(
NR_PSTR("Laravel\\Lumen\\Application::sendExceptionToHandler"),
nr_lumen_exception TSRMLS_CC);
}
3 changes: 2 additions & 1 deletion agent/php_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ static const nr_framework_table_t all_frameworks[] = {
{"Laravel", "laravel", "bootstrap/cache/compiled.php", 0, nr_laravel_enable,
NR_FW_LARAVEL}, /* 5.1.0-x */

{"Lumen", "lumen", "lumen-framework/src/helpers.php", 0, nr_lumen_enable, NR_FW_LUMEN},

{"Magento", "magento", "app/mage.php", 0, nr_magento1_enable,
NR_FW_MAGENTO1},
{"Magento2", "magento2", "magento/framework/app/bootstrap.php", 0,
Expand Down Expand Up @@ -518,7 +520,6 @@ static nr_library_table_t libraries[] = {
{"CakePHP3", "cakephp/src/core/functions.php", NULL},
{"Fuel", "fuel/core/classes/fuel.php", NULL},
{"Lithium", "lithium/core/libraries.php", NULL},
{"Lumen", "lumen-framework/src/helpers.php", NULL},
{"Phpbb", "phpbb/request/request.php", NULL},
{"Phpixie2", "phpixie/core/classes/phpixie/pixie.php", NULL},
{"Phpixie3", "phpixie/framework.php", NULL},
Expand Down
1 change: 1 addition & 0 deletions agent/php_newrelic.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ typedef enum {
NR_FW_JOOMLA,
NR_FW_KOHANA,
NR_FW_LARAVEL,
NR_FW_LUMEN,
NR_FW_MAGENTO1,
NR_FW_MAGENTO2,
NR_FW_MEDIAWIKI,
Expand Down