Skip to content

Instantly share code, notes, and snippets.

@krakjoe
Created March 20, 2019 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krakjoe/9c11289b3f8ff64a6c0c64d3b8bbc02c to your computer and use it in GitHub Desktop.
Save krakjoe/9c11289b3f8ff64a6c0c64d3b8bbc02c to your computer and use it in GitHub Desktop.
reload
dnl config.m4 for extension reload
PHP_ARG_ENABLE([reload],
[whether to enable reload support],
[AS_HELP_STRING([--enable-reload],
[Enable reload support])],
[no])
if test "$PHP_RELOAD" != "no"; then
AC_DEFINE(HAVE_RELOAD, 1, [ Have reload support ])
PHP_NEW_EXTENSION(reload, reload.c, $ext_shared)
fi
/* reload extension for PHP */
#ifndef PHP_RELOAD_H
# define PHP_RELOAD_H
extern zend_module_entry reload_module_entry;
# define phpext_reload_ptr &reload_module_entry
# define PHP_RELOAD_VERSION "0.1.0"
# if defined(ZTS) && defined(COMPILE_DL_RELOAD)
ZEND_TSRMLS_CACHE_EXTERN()
# endif
#endif /* PHP_RELOAD_H */
/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: krakjoe |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_reload.h"
#include "zend_closures.h"
typedef struct _zend_closure_t {
zend_object std;
zend_function func;
zval this_ptr;
zend_class_entry *called_scope;
#if PHP_VERSION_ID <= 70200
void (*orig_internal_handler)(INTERNAL_FUNCTION_PARAMETERS);
#else
zif_handler orig_internal_handler;
#endif
} zend_closure_t;
typedef struct _php_reload_tables_t {
HashTable *classes;
HashTable *functions;
} php_reload_tables_t;
static zend_always_inline int php_reload_include(php_reload_tables_t *tables, zval *filename, zval *return_value) {
php_reload_tables_t backup;
zend_op_array *ops;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fcc = empty_fcall_info_cache;
ALLOC_HASHTABLE(tables->classes);
zend_hash_init(tables->classes, 8, NULL, NULL, 0);
ALLOC_HASHTABLE(tables->functions);
zend_hash_init(tables->functions, 8, NULL, NULL, 0);
backup.classes = CG(class_table);
backup.functions = CG(function_table);
CG(class_table) = tables->classes;
CG(function_table) = tables->functions;
ops = compile_filename(ZEND_INCLUDE, filename);
CG(class_table) = backup.classes;
CG(function_table) = backup.functions;
if (!ops) {
zend_hash_destroy(tables->classes);
zend_hash_destroy(tables->functions);
FREE_HASHTABLE(tables->classes);
FREE_HASHTABLE(tables->functions);
return FAILURE;
}
fci.size = sizeof(zend_fcall_info);
fci.retval = return_value;
fcc.function_handler = (zend_function*) ops;
ZEND_MAP_PTR_INIT(ops->run_time_cache, ecalloc(1, ops->cache_size));
if (zend_call_function(&fci, &fcc) != SUCCESS) {
zend_hash_destroy(tables->classes);
zend_hash_destroy(tables->functions);
FREE_HASHTABLE(tables->classes);
FREE_HASHTABLE(tables->functions);
return FAILURE;
}
destroy_op_array(ops);
efree(ops);
return SUCCESS;
}
static zend_always_inline void php_reload_relink(zend_objects_store *objects, zend_class_entry *replaced, zend_class_entry *ce) {
if (objects->top > 1) {
uint32_t it = 1,
end = objects->top;
while (it < end) {
zend_object *object = objects->object_buckets[it];
if (IS_OBJ_VALID(object)) {
if (object->ce == replaced) {
object->ce = ce;
} else if (instanceof_function(object->ce, zend_ce_closure)) {
zend_closure_t *closure = (zend_closure_t*) object;
if (RUN_TIME_CACHE(&closure->func.op_array)) {
memset(RUN_TIME_CACHE(&closure->func.op_array), 0, closure->func.op_array.cache_size);
}
if (closure->called_scope == replaced) {
closure->called_scope = ce;
}
}
}
it++;
}
}
}
static zend_always_inline void php_reload_classes(HashTable *classes) {
zend_string *key;
zend_class_entry *ce, *edit;
zend_ulong address;
HashTable replaced;
zend_hash_init(&replaced, 8, NULL, NULL, 0);
ZEND_HASH_FOREACH_STR_KEY_PTR(classes, key, ce) {
zend_class_entry *old = zend_hash_find_ptr(CG(class_table), key);
if (!old) {
zend_hash_add_ptr(CG(class_table), key, ce);
} else {
zend_hash_index_update_ptr(
&replaced, (zend_ulong) old, ce);
/* TODO verify interface of new class matches old class */
zend_hash_update_ptr(CG(class_table), key, ce);
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_FOREACH_NUM_KEY_PTR(&replaced, address, ce) {
zend_class_entry *replaced = (zend_class_entry*) address;
zend_function *method;
ZEND_HASH_FOREACH_PTR(CG(class_table), edit) {
if (edit->parent == replaced) {
edit->parent = ce;
}
/* TODO interfaces, traits ? */
ZEND_HASH_FOREACH_PTR(&edit->function_table, method) {
zend_arg_info *info,
*end;
if (method->common.scope == replaced) {
method->common.scope = ce;
}
if (ZEND_USER_CODE(method->type)) {
if (RUN_TIME_CACHE(&method->op_array)) {
memset(
RUN_TIME_CACHE(&method->op_array),
0,
method->op_array.cache_size);
}
}
info = method->common.arg_info;
if (method->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
info--;
}
end = method->common.arg_info + method->common.num_args;
if (method->common.fn_flags & ZEND_ACC_VARIADIC) {
end++;
}
while (info < end) {
if (ZEND_TYPE_IS_SET(info->type) && ZEND_TYPE_IS_CLASS(info->type)) {
zend_string *name = ZEND_TYPE_IS_NAME(info->type) ?
ZEND_TYPE_NAME(info->type) :
ZEND_TYPE_CE(info->type)->name;
if (zend_string_equals_ci(name, replaced->name)) {
info->type = ZEND_TYPE_ENCODE_CLASS(
replaced->name,
ZEND_TYPE_ALLOW_NULL(info->type));
}
}
info++;
}
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
php_reload_relink(&EG(objects_store), replaced, ce);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&replaced);
}
static zend_always_inline void php_reload_functions(HashTable *functions) {
/* unimplemented */
}
/* {{{ void reload(string file)
*/
PHP_FUNCTION(reload)
{
zval *file;
php_reload_tables_t tables;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &file) != SUCCESS) {
return;
}
if (!file || Z_TYPE_P(file) != IS_STRING) {
return;
}
if (php_reload_include(&tables, file, return_value) != SUCCESS) {
return;
}
php_reload_classes(tables.classes);
php_reload_functions(tables.functions);
zend_hash_destroy(tables.classes);
FREE_HASHTABLE(tables.classes);
zend_hash_destroy(tables.functions);
FREE_HASHTABLE(tables.functions);
}
/* }}} */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(reload)
{
#if defined(ZTS) && defined(COMPILE_DL_RELOAD)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(reload)
{
php_info_print_table_start();
php_info_print_table_header(2, "reload support", "enabled");
php_info_print_table_end();
}
/* }}} */
/* {{{ arginfo
*/
ZEND_BEGIN_ARG_INFO_EX(reload_arginfo, 0, 0, 1)
ZEND_ARG_INFO(0, file)
ZEND_END_ARG_INFO()
/* }}} */
/* {{{ reload_functions[]
*/
static const zend_function_entry reload_functions[] = {
PHP_FE(reload, reload_arginfo)
PHP_FE_END
};
/* }}} */
/* {{{ reload_module_entry
*/
zend_module_entry reload_module_entry = {
STANDARD_MODULE_HEADER,
"reload", /* Extension name */
reload_functions, /* zend_function_entry */
NULL, /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(reload), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(reload), /* PHP_MINFO - Module info */
PHP_RELOAD_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_RELOAD
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(reload)
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment