Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
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
95 changes: 94 additions & 1 deletion agent/fw_drupal8.c
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,96 @@ NR_PHP_WRAPPER(nr_drupal94_invoke_all_with_clean) {
NR_PHP_WRAPPER_END
#endif // OAPI

static bool nr_is_invalid_key_val_arr(nr_php_string_hash_key_t* key,
zval* val) {
if (NULL == key || 0 == ZEND_STRING_LEN(key)
|| 0 == nr_php_is_zval_valid_array(val)
|| 0 == zend_hash_num_elements(Z_ARRVAL_P(val))) {
return true;
} else {
return false;
}
}

/*
* Purpose: Instrument Drupal Attribute Hooks for Drupal 11.1+
*
* Params: 1. A zval pointer to the moduleHandler instance in use by Drupal.
*
* Return: bool
*
*/
static bool nr_drupal_hook_attribute_instrument(zval* module_handler) {
zval* hook_implementation_map = NULL;

nr_php_string_hash_key_t* hook_key = NULL;
zval* hook_val = NULL;
nr_php_string_hash_key_t* class_key = NULL;
zval* class_val = NULL;
nr_php_string_hash_key_t* method_key = NULL;
zval* module_val = NULL;

char* hookpath = NULL;

hook_implementation_map = nr_php_get_zval_object_property(
module_handler, "hookImplementationsMap");

if (!nr_php_is_zval_valid_array(hook_implementation_map)) {
nrl_verbosedebug(NRL_FRAMEWORK,
"hookImplementationsMap property not a valid array");
return false;
}

ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hook_implementation_map), hook_key,
hook_val) {
if (nr_is_invalid_key_val_arr(hook_key, hook_val)) {
nrl_warning(NRL_FRAMEWORK,
"hookImplementationsMap[hook]: invalid key or value");
return false;
}

ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(hook_val), class_key, class_val) {
if (nr_is_invalid_key_val_arr(class_key, class_val)) {
nrl_warning(NRL_FRAMEWORK,
"hookImplementationsMap[class]: invalid key or value");
return false;
}

ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(class_val), method_key,
module_val) {
if (NULL == method_key
|| 0 == nr_php_is_zval_valid_string(module_val)) {
nrl_warning(NRL_FRAMEWORK,
"hookImplementationsMap[method]: invalid key or value");
return false;
}

if (0
== nr_striendswith(
ZEND_STRING_VALUE(class_key), ZEND_STRING_LEN(class_key),
NR_PSTR("Drupal\\Core\\Extension\\ProceduralCall"))) {
hookpath = nr_formatf("%s", ZEND_STRING_VALUE(method_key));
} else {
hookpath = nr_formatf("%s::%s", ZEND_STRING_VALUE(class_key),
ZEND_STRING_VALUE(method_key));
}

nr_php_wrap_user_function_drupal(
hookpath, nr_strlen(hookpath), Z_STRVAL_P(module_val),
Z_STRLEN_P(module_val), ZEND_STRING_VALUE(hook_key),
ZEND_STRING_LEN(hook_key));

nr_free(hookpath);
}
ZEND_HASH_FOREACH_END();
}
ZEND_HASH_FOREACH_END();
}
ZEND_HASH_FOREACH_END();

return true;
}

/*
* Purpose : Wrap the invoke() method of the module handler instance in use.
*/
Expand Down Expand Up @@ -635,6 +725,9 @@ NR_PHP_WRAPPER(nr_drupal8_module_handler) {

ce = Z_OBJCE_P(*retval_ptr);

if (nr_drupal_hook_attribute_instrument(*retval_ptr)) {
NR_PHP_WRAPPER_LEAVE;
}
nr_drupal8_add_method_callback(ce, NR_PSTR("getimplementations"),
nr_drupal8_post_get_implementations TSRMLS_CC);
nr_drupal8_add_method_callback(ce, NR_PSTR("implementshook"),
Expand All @@ -643,7 +736,7 @@ NR_PHP_WRAPPER(nr_drupal8_module_handler) {
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
nr_drupal8_add_method_callback_before_after_clean(
ce, NR_PSTR("invokeAllWith"), nr_drupal94_invoke_all_with,
ce, NR_PSTR("invokeallwith"), nr_drupal94_invoke_all_with,
nr_drupal94_invoke_all_with_after, nr_drupal94_invoke_all_with_clean);
#else
nr_drupal8_add_method_callback(ce, NR_PSTR("invokeallwith"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when key value is an empty array */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
'hookname_b' => array(),
'hookname_c' => array('classname_c' => array('methodname_c' => 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when key is an empty string */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
'hookname_b' => array('' => array('methodname_b' => 'modulename_b')),
'hookname_c' => array('classname_c' => array('methodname_c' => 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when map key is not a string */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
1 => array('classname_b' => array('methodname_b' => 'modulename_b')),
'hookname_c' => array('classname_c', array('methodname_c', 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when hookImplemementationsMap is not an array */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected string $hookImplementationsMap = 'just a string';

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when module name is not a string */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
'hookname_b' => array('classname_b' => array('methodname_b' => array(1, 2, 3))),
'hookname_c' => array('classname_c' => array('methodname_c' => 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior when key value is not an array */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
'hookname_b' => array('classname_b' => 'just a string'),
'hookname_c' => array('classname_c' => array('methodname_c' => 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
34 changes: 34 additions & 0 deletions tests/integration/frameworks/drupal/mock_module_handler_valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/* Verify agent behavior on valid hookImplementationsMap */

namespace Drupal\Core\Extension {
interface ModuleHandlerInterface
{
public function invokeAllWith($hook_str, $callback);
}
class ModuleHandler implements ModuleHandlerInterface
{
protected array $hookImplementationsMap = array(
'hookname' => array('classname' => array('methodname' => 'modulename')),
'hookname_b' => array('classname_b' => array('methodname_b' => 'modulename_b')),
'hookname_c' => array('classname_c' => array('methodname_c' => 'modulename_c')),
);

// to avoid editor warnings
public function invokeAllWith($hook_str, $callback)
{
return null;
}

// for debugging purposes
public function dump()
{
var_dump($this->hookImplementationsMap);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/*DESCRIPTION
Verify agent behavior when key value is an empty array
*/

/*SKIPIF
<?php
if (version_compare(PHP_VERSION, '7.4', '<')) {
die("skip: PHP >= 7.4 required\n");
}
*/

/*INI
newrelic.framework = drupal8
*/

/*EXPECT_TRACED_ERRORS null */

/*EXPECT_ERROR_EVENTS null */

/*EXPECT
*/

require_once __DIR__ . '/mock_module_handler_empty_array.php';

// This specific API is needed for us to instrument the ModuleHandler
class Drupal
{
public function moduleHandler()
{
return new Drupal\Core\Extension\ModuleHandler();
}
}

// Create module handler
$drupal = new Drupal();
$handler = $drupal->moduleHandler();
Loading