Skip to content

Commit 191efc8

Browse files
committed
Allow hooking an inherited internal constructor
Signed-off-by: Bob Weinand <[email protected]>
1 parent b29bcf1 commit 191efc8

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test hooking inherited internal constructors via install_hook()
3+
--FILE--
4+
<?php
5+
6+
\DDTrace\install_hook("X::__construct", function() { echo "hook 1\n"; });
7+
8+
if (true) {
9+
class X extends ArrayObject {}
10+
}
11+
12+
\DDTrace\install_hook("X::__construct", function() { echo "hook 2\n"; });
13+
14+
$a = (new X);
15+
$a->__construct();
16+
new ArrayObject; // must not trigger the hook
17+
18+
?>
19+
--EXPECT--
20+
hook 1
21+
hook 2
22+
hook 1
23+
hook 2

zend_abstract_interface/hook/hook.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,12 @@ static inline void zai_hook_resolved_install_inherited_internal_function_recursi
507507
}
508508

509509
static zend_long zai_hook_resolved_install(zai_hook_t *hook, zend_function *resolved, zend_class_entry *ce) {
510+
// Handle the edge case of inherited internal constructors: They are not fetched from the ce->function_table, but from ce->construct
511+
// Hence we need to explicitly instrument the parents constructor (this will also implicitly instrument the inherited constructor for explicit calls).
512+
if ((resolved->common.fn_flags & ZEND_ACC_CTOR) && !ZEND_USER_CODE(resolved->type) && resolved->common.scope->constructor) {
513+
resolved = resolved->common.scope->constructor;
514+
}
515+
510516
zai_hooks_entry *hooks = zai_hook_resolved_ensure_hooks_entry(resolved, ce);
511517
zend_long index = zai_hook_add_entry(hooks, hook);
512518

@@ -724,6 +730,12 @@ static inline void zai_hook_resolve(HashTable *base_ht, zend_class_entry *ce, ze
724730
return;
725731
}
726732

733+
// Handle the edge case of inherited internal constructors: They are not fetched from the ce->function_table, but from ce->construct
734+
// Hence we need to explicitly instrument the parents constructor (this will also implicitly instrument the inherited constructor for explicit calls).
735+
if ((function->common.fn_flags & ZEND_ACC_CTOR) && !ZEND_USER_CODE(function->type) && function->common.scope->constructor) {
736+
function = function->common.scope->constructor;
737+
}
738+
727739
zai_install_address addr = zai_hook_install_address(function);
728740
if (!zend_hash_index_add_ptr(&zai_hook_resolved, addr, hooks)) {
729741
// it's already there (e.g. thanks to aliases, traits, ...), merge it
@@ -737,6 +749,12 @@ static inline void zai_hook_resolve(HashTable *base_ht, zend_class_entry *ce, ze
737749
existingHooks->dynamic += hook->dynamic;
738750
zend_hash_index_add_new(&existingHooks->hooks, index, hook_zv);
739751
zai_hook_sort_newest(existingHooks);
752+
753+
if (hook->is_abstract) {
754+
zai_hook_resolved_install_abstract_recursive(hook, (zend_ulong)index, function->common.scope);
755+
} else if (!ZEND_USER_CODE(function->type) && function->common.scope) {
756+
zai_hook_resolved_install_inherited_internal_function_recursive(hook, (zend_ulong)index, function->common.scope, function->internal_function.handler);
757+
}
740758
} ZEND_HASH_FOREACH_END();
741759

742760
// we remove the whole zai_hooks_entry, excluding the individual zai_hook_t which we moved
@@ -755,9 +773,16 @@ static inline void zai_hook_resolve(HashTable *base_ht, zend_class_entry *ce, ze
755773
base_ht->pDestructor = zai_hook_hash_destroy;
756774
zai_hook_resolve_hooks_entry(hooks, function);
757775
zai_hook_t *hook;
758-
ZEND_HASH_FOREACH_PTR(&hooks->hooks, hook) {
776+
zend_ulong index;
777+
ZEND_HASH_FOREACH_NUM_KEY_PTR(&hooks->hooks, index, hook) {
759778
hook->resolved_scope = ce;
760779
hook->is_abstract = is_abstract;
780+
781+
if (hook->is_abstract) {
782+
zai_hook_resolved_install_abstract_recursive(hook, (zend_ulong)index, function->common.scope);
783+
} else if (!ZEND_USER_CODE(function->type) && function->common.scope) {
784+
zai_hook_resolved_install_inherited_internal_function_recursive(hook, (zend_ulong)index, function->common.scope, function->internal_function.handler);
785+
}
761786
} ZEND_HASH_FOREACH_END();
762787
}
763788
}

0 commit comments

Comments
 (0)