mirror of
https://github.com/php/php-src.git
synced 2024-11-24 18:34:21 +08:00
Add an API to observe functions and classes being linked
To observe when the functions and classes start being officially available to the user Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
This commit is contained in:
parent
9b984f52ea
commit
bf427b732a
@ -31,6 +31,7 @@
|
||||
#include "zend_inheritance.h"
|
||||
#include "zend_ini.h"
|
||||
#include "zend_enum.h"
|
||||
#include "zend_observer.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
@ -3263,6 +3264,7 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
ce->refcount++;
|
||||
}
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
return SUCCESS;
|
||||
}
|
||||
return FAILURE;
|
||||
|
@ -1093,6 +1093,7 @@ ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{
|
||||
if (func->common.function_name) {
|
||||
zend_string_addref(func->common.function_name);
|
||||
}
|
||||
zend_observer_function_declared_notify(&func->op_array, Z_STR_P(lcname));
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@ -1116,12 +1117,14 @@ ZEND_API zend_class_entry *zend_bind_class_in_slot(
|
||||
}
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_LINKED) {
|
||||
zend_observer_class_linked_notify(ce, Z_STR_P(lcname));
|
||||
return ce;
|
||||
}
|
||||
|
||||
ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname));
|
||||
if (ce) {
|
||||
ZEND_ASSERT(!EG(exception));
|
||||
zend_observer_class_linked_notify(ce, Z_STR_P(lcname));
|
||||
return ce;
|
||||
}
|
||||
|
||||
@ -7245,6 +7248,7 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as
|
||||
if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) {
|
||||
do_bind_function_error(lcname, op_array, 1);
|
||||
}
|
||||
zend_observer_function_declared_notify(op_array, lcname);
|
||||
zend_string_release_ex(lcname, 0);
|
||||
return;
|
||||
}
|
||||
@ -7877,6 +7881,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
|
||||
zend_string_release(lcname);
|
||||
zend_build_properties_info_table(ce);
|
||||
ce->ce_flags |= ZEND_ACC_LINKED;
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
return;
|
||||
}
|
||||
} else if (!extends_ast) {
|
||||
|
@ -1181,6 +1181,9 @@ END_EXTERN_C()
|
||||
/* this flag is set when compiler invoked during preloading in separate process */
|
||||
#define ZEND_COMPILE_PRELOAD_IN_CHILD (1<<17)
|
||||
|
||||
/* ignore observer notifications, e.g. to manually notify afterwards in a post-processing step after compilation */
|
||||
#define ZEND_COMPILE_IGNORE_OBSERVER (1<<18)
|
||||
|
||||
/* The default value for CG(compiler_options) */
|
||||
#define ZEND_COMPILE_DEFAULT ZEND_COMPILE_HANDLE_OP_ARRAY
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "zend_enum.h"
|
||||
#include "zend_attributes.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_observer.h"
|
||||
|
||||
ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL;
|
||||
ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL;
|
||||
@ -3134,18 +3135,24 @@ static zend_always_inline bool register_early_bound_ce(zval *delayed_early_bindi
|
||||
if (EXPECTED(!(ce->ce_flags & ZEND_ACC_PRELOADED))) {
|
||||
if (zend_hash_set_bucket_key(EG(class_table), (Bucket *)delayed_early_binding, lcname) != NULL) {
|
||||
Z_CE_P(delayed_early_binding) = ce;
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* If preloading is used, don't replace the existing bucket, add a new one. */
|
||||
if (zend_hash_add_ptr(EG(class_table), lcname, ce) != NULL) {
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
|
||||
return false;
|
||||
}
|
||||
return zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL;
|
||||
if (zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL) {
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */
|
||||
|
@ -32,12 +32,17 @@
|
||||
(ZEND_MAP_PTR(function->common.run_time_cache) && !(function->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))
|
||||
|
||||
zend_llist zend_observers_fcall_list;
|
||||
zend_llist zend_observer_function_declared_callbacks;
|
||||
zend_llist zend_observer_class_linked_callbacks;
|
||||
zend_llist zend_observer_error_callbacks;
|
||||
zend_llist zend_observer_fiber_init;
|
||||
zend_llist zend_observer_fiber_switch;
|
||||
zend_llist zend_observer_fiber_destroy;
|
||||
|
||||
int zend_observer_fcall_op_array_extension;
|
||||
bool zend_observer_errors_observed;
|
||||
bool zend_observer_function_declared_observed;
|
||||
bool zend_observer_class_linked_observed;
|
||||
|
||||
ZEND_TLS zend_execute_data *current_observed_frame;
|
||||
|
||||
@ -51,6 +56,8 @@ ZEND_API void zend_observer_fcall_register(zend_observer_fcall_init init)
|
||||
ZEND_API void zend_observer_startup(void)
|
||||
{
|
||||
zend_llist_init(&zend_observers_fcall_list, sizeof(zend_observer_fcall_init), NULL, 1);
|
||||
zend_llist_init(&zend_observer_function_declared_callbacks, sizeof(zend_observer_function_declared_cb), NULL, 1);
|
||||
zend_llist_init(&zend_observer_class_linked_callbacks, sizeof(zend_observer_class_linked_cb), NULL, 1);
|
||||
zend_llist_init(&zend_observer_error_callbacks, sizeof(zend_observer_error_cb), NULL, 1);
|
||||
zend_llist_init(&zend_observer_fiber_init, sizeof(zend_observer_fiber_init_handler), NULL, 1);
|
||||
zend_llist_init(&zend_observer_fiber_switch, sizeof(zend_observer_fiber_switch_handler), NULL, 1);
|
||||
@ -100,6 +107,8 @@ ZEND_API void zend_observer_activate(void)
|
||||
ZEND_API void zend_observer_shutdown(void)
|
||||
{
|
||||
zend_llist_destroy(&zend_observers_fcall_list);
|
||||
zend_llist_destroy(&zend_observer_function_declared_callbacks);
|
||||
zend_llist_destroy(&zend_observer_class_linked_callbacks);
|
||||
zend_llist_destroy(&zend_observer_error_callbacks);
|
||||
zend_llist_destroy(&zend_observer_fiber_init);
|
||||
zend_llist_destroy(&zend_observer_fiber_switch);
|
||||
@ -286,8 +295,45 @@ ZEND_API void zend_observer_fcall_end_all(void)
|
||||
EG(current_execute_data) = original_execute_data;
|
||||
}
|
||||
|
||||
ZEND_API void zend_observer_function_declared_register(zend_observer_function_declared_cb cb)
|
||||
{
|
||||
zend_observer_function_declared_observed = true;
|
||||
zend_llist_add_element(&zend_observer_function_declared_callbacks, &cb);
|
||||
}
|
||||
|
||||
ZEND_API void ZEND_FASTCALL zend_observer_function_declared_notify(zend_op_array *op_array, zend_string *name)
|
||||
{
|
||||
if (CG(compiler_options) & ZEND_COMPILE_IGNORE_OBSERVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (zend_llist_element *element = zend_observer_function_declared_callbacks.head; element; element = element->next) {
|
||||
zend_observer_function_declared_cb callback = *(zend_observer_function_declared_cb *) (element->data);
|
||||
callback(op_array, name);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void zend_observer_class_linked_register(zend_observer_class_linked_cb cb)
|
||||
{
|
||||
zend_observer_class_linked_observed = true;
|
||||
zend_llist_add_element(&zend_observer_class_linked_callbacks, &cb);
|
||||
}
|
||||
|
||||
ZEND_API void ZEND_FASTCALL zend_observer_class_linked_notify(zend_class_entry *ce, zend_string *name)
|
||||
{
|
||||
if (CG(compiler_options) & ZEND_COMPILE_IGNORE_OBSERVER) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (zend_llist_element *element = zend_observer_class_linked_callbacks.head; element; element = element->next) {
|
||||
zend_observer_class_linked_cb callback = *(zend_observer_class_linked_cb *) (element->data);
|
||||
callback(ce, name);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void zend_observer_error_register(zend_observer_error_cb cb)
|
||||
{
|
||||
zend_observer_errors_observed = true;
|
||||
zend_llist_add_element(&zend_observer_error_callbacks, &cb);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
extern ZEND_API int zend_observer_fcall_op_array_extension;
|
||||
extern ZEND_API bool zend_observer_errors_observed;
|
||||
extern ZEND_API bool zend_observer_function_declared_observed;
|
||||
extern ZEND_API bool zend_observer_class_linked_observed;
|
||||
|
||||
#define ZEND_OBSERVER_ENABLED (zend_observer_fcall_op_array_extension != -1)
|
||||
|
||||
@ -80,6 +83,14 @@ ZEND_API void ZEND_FASTCALL zend_observer_fcall_end(
|
||||
|
||||
ZEND_API void zend_observer_fcall_end_all(void);
|
||||
|
||||
typedef void (*zend_observer_function_declared_cb)(zend_op_array *op_array, zend_string *name);
|
||||
typedef void (*zend_observer_class_linked_cb)(zend_class_entry *ce, zend_string *name);
|
||||
|
||||
ZEND_API void zend_observer_function_declared_register(zend_observer_function_declared_cb cb);
|
||||
ZEND_API void ZEND_FASTCALL zend_observer_function_declared_notify(zend_op_array *op_array, zend_string *name);
|
||||
ZEND_API void zend_observer_class_linked_register(zend_observer_class_linked_cb cb);
|
||||
ZEND_API void ZEND_FASTCALL zend_observer_class_linked_notify(zend_class_entry *ce, zend_string *name);
|
||||
|
||||
typedef void (*zend_observer_error_cb)(int type, zend_string *error_filename, uint32_t error_lineno, zend_string *message);
|
||||
|
||||
ZEND_API void zend_observer_error_register(zend_observer_error_cb callback);
|
||||
|
@ -1806,6 +1806,7 @@ static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handl
|
||||
CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
|
||||
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
|
||||
CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
|
||||
CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER;
|
||||
if (ZCG(accel_directives).file_cache) {
|
||||
CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "zend_accelerator_util_funcs.h"
|
||||
#include "zend_persist.h"
|
||||
#include "zend_shared_alloc.h"
|
||||
#include "zend_observer.h"
|
||||
|
||||
typedef int (*id_function_t)(void *, void *);
|
||||
typedef void (*unique_copy_ctor_func_t)(void *pElement);
|
||||
@ -145,6 +146,8 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
|
||||
Bucket *p, *end;
|
||||
zval *t;
|
||||
|
||||
bool call_observers = zend_observer_function_declared_observed;
|
||||
|
||||
zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
|
||||
p = source->arData;
|
||||
end = p + source->nNumUsed;
|
||||
@ -156,6 +159,9 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source)
|
||||
goto failure;
|
||||
}
|
||||
_zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1);
|
||||
if (UNEXPECTED(call_observers) && *ZSTR_VAL(p->key)) { // if not rtd key
|
||||
zend_observer_function_declared_notify(Z_PTR(p->val), p->key);
|
||||
}
|
||||
}
|
||||
target->nInternalPointer = 0;
|
||||
return;
|
||||
@ -182,6 +188,8 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
|
||||
Bucket *p, *end;
|
||||
zval *t;
|
||||
|
||||
bool call_observers = zend_observer_class_linked_observed;
|
||||
|
||||
zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0);
|
||||
p = source->arData;
|
||||
end = p + source->nNumUsed;
|
||||
@ -221,6 +229,9 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
|
||||
&& ZSTR_HAS_CE_CACHE(ce->name)
|
||||
&& ZSTR_VAL(p->key)[0]) {
|
||||
ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0);
|
||||
if (UNEXPECTED(call_observers)) {
|
||||
zend_observer_class_linked_notify(ce, p->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +256,18 @@ static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context
|
||||
}
|
||||
}
|
||||
|
||||
void declared_function_observer(zend_op_array *op_array, zend_string *name) {
|
||||
if (ZT_G(observer_observe_declaring)) {
|
||||
php_printf("%*s<!-- declared function '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(name));
|
||||
}
|
||||
}
|
||||
|
||||
void declared_class_observer(zend_class_entry *ce, zend_string *name) {
|
||||
if (ZT_G(observer_observe_declaring)) {
|
||||
php_printf("%*s<!-- declared class '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(name));
|
||||
}
|
||||
}
|
||||
|
||||
static ZEND_INI_MH(zend_test_observer_OnUpdateCommaList)
|
||||
{
|
||||
zend_array **p = (zend_array **) ZEND_INI_GET_ADDR();
|
||||
@ -301,6 +313,7 @@ PHP_INI_BEGIN()
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_all", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_all, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_includes", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_includes, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_functions, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.observe_declaring", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_declaring, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_ENTRY("zend_test.observer.observe_function_names", "", PHP_INI_ALL, zend_test_observer_OnUpdateCommaList, observer_observe_function_names, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_type", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_type, zend_zend_test_globals, zend_test_globals)
|
||||
STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_value", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_value, zend_zend_test_globals, zend_test_globals)
|
||||
@ -334,6 +347,9 @@ void zend_test_observer_init(INIT_FUNC_ARGS)
|
||||
zend_observer_fiber_switch_register(fiber_enter_observer);
|
||||
zend_observer_fiber_switch_register(fiber_suspend_observer);
|
||||
zend_observer_fiber_destroy_register(fiber_destroy_observer);
|
||||
|
||||
zend_observer_function_declared_register(declared_function_observer);
|
||||
zend_observer_class_linked_register(declared_class_observer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test)
|
||||
int observer_observe_all;
|
||||
int observer_observe_includes;
|
||||
int observer_observe_functions;
|
||||
int observer_observe_declaring;
|
||||
zend_array *observer_observe_function_names;
|
||||
int observer_show_return_type;
|
||||
int observer_show_return_value;
|
||||
|
54
ext/zend_test/tests/observer_declarations_01.phpt
Normal file
54
ext/zend_test/tests/observer_declarations_01.phpt
Normal file
@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
Observer: Observe function and class declarations
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--INI--
|
||||
zend_test.observer.enabled=1
|
||||
zend_test.observer.observe_all=1
|
||||
zend_test.observer.observe_declaring=1
|
||||
--FILE--
|
||||
<?php
|
||||
function foo()
|
||||
{
|
||||
echo "foo\n";
|
||||
}
|
||||
|
||||
class A {
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
}
|
||||
|
||||
if (time() > 0) {
|
||||
function nested()
|
||||
{
|
||||
}
|
||||
|
||||
class C
|
||||
{
|
||||
}
|
||||
|
||||
class D extends A
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
foo();
|
||||
?>
|
||||
--EXPECTF--
|
||||
<!-- declared function 'foo' -->
|
||||
<!-- declared class 'a' -->
|
||||
<!-- declared class 'b' -->
|
||||
<!-- init '%s' -->
|
||||
<file '%s'>
|
||||
<!-- init time() -->
|
||||
<time>
|
||||
</time>
|
||||
<!-- declared function 'nested' -->
|
||||
<!-- declared class 'c' -->
|
||||
<!-- declared class 'd' -->
|
||||
<!-- init foo() -->
|
||||
<foo>
|
||||
foo
|
||||
</foo>
|
||||
</file '%s'>
|
Loading…
Reference in New Issue
Block a user