Browse files

All ways of adding and removing magic methods and old-style construct…

…ors were reworked and corrected (issue #35).

Magic methods __isset, __unset, __callStatic, and __toString are now supported.
Functions runkit_class_adopt & runkit_class_emancipate now change class-hierarchy (issue #13).
tony2001's patch d63c984 was applied and reworked.
New tests were added.
  • Loading branch information...
1 parent 1a3af5e commit 7f5b7b16c5fc3ed97370a79822785c657e0ddade @zenovich committed Sep 17, 2012
View
12 package.xml
@@ -32,13 +32,17 @@ Execute code in restricted environment (sandboxing).
+ PHP 5.4 is now fully supported
+ Introduced new function "runkit_default_property_remove" (Thanks to Juhan Kundla)
+ Ability to add non-scalar default properties was added
+ + Magic methods __isset, __unset, __callStatic, and __toString are now supported.
+ + Functions runkit_class_adopt & runkit_class_emancipate now change class-hierarchy (issue #13)
Critical fixes:
* Crash on sandbox creation when register_globals is switched on was fixed
* Building and working with PHP4 were fixed
Fixes:
* Crash on syntax error in source file importing with runkit_import was fixed
+ * All ways of adding and removing magic methods and old-style constructors
+ were reworked and corrected (Thanks to Anthony Dovgal for issue #35).
* Side affect of redefining, adding and removing class constant (Issue #25) was fixed
* Adding of non-lowercase default properties was fixed (removed lowercasing)
* Skip the leading slash in class names
@@ -60,6 +64,14 @@ Execute code in restricted environment (sandboxing).
<contents>
<dir name="/">
<dir name="tests">
+ <file name="runkit_add_magic_methods.phpt" role="test" />
+ <file name="runkit_add_old_style_ctor.phpt" role="test" />
+ <file name="runkit_add_old_style_ctor1.phpt" role="test" />
+ <file name="runkit_add_old_style_ctor_by_adopting.phpt" role="test" />
+ <file name="runkit_add_old_style_ctor_by_copying.phpt" role="test" />
+ <file name="runkit_add_old_style_ctor_by_importing.inc" role="test" />
+ <file name="runkit_add_old_style_ctor_by_importing.phpt" role="test" />
+ <file name="runkit_adopt_emancipate_and_inheritance.phpt" role="test" />
<file name="runkit_class_adopt.phpt" role="test" />
<file name="runkit_class_emancipate.phpt" role="test" />
<file name="runkit_constant_add.phpt" role="test" />
View
173 php_runkit.h
@@ -338,28 +338,167 @@ struct _php_runkit_sandbox_object {
}
#ifdef ZEND_ENGINE_2
-#define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, method, fe) { \
- if ((strcmp((method), (ce)->name) == 0) || \
- (strcmp((method), "__construct") == 0)) { (ce)->constructor = (fe); (fe)->common.fn_flags = ZEND_ACC_CTOR; } \
- else if (strcmp((method), "__destruct") == 0) { (ce)->destructor = (fe); (fe)->common.fn_flags = ZEND_ACC_DTOR; } \
- else if (strcmp((method), "__clone") == 0) { (ce)->clone = (fe); (fe)->common.fn_flags = ZEND_ACC_CLONE; } \
- else if (strcmp((method), "__get") == 0) (ce)->__get = (fe); \
- else if (strcmp((method), "__set") == 0) (ce)->__set = (fe); \
- else if (strcmp((method), "__call") == 0) (ce)->__call = (fe); \
+#if RUNKIT_ABOVE53
+# define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, lcmname, mname_len, fe, orig_fe) { \
+ if (!strncmp((lcmname), ZEND_CLONE_FUNC_NAME, (mname_len))) { \
+ (ce)->clone = (fe); (fe)->common.fn_flags |= ZEND_ACC_CLONE; \
+ } else if (!strncmp((lcmname), ZEND_CONSTRUCTOR_FUNC_NAME, (mname_len))) { \
+ if (!(ce)->constructor || (ce)->constructor == (orig_fe)) { \
+ (ce)->constructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_CTOR; \
+ } \
+ } else if (!strncmp((lcmname), ZEND_DESTRUCTOR_FUNC_NAME, (mname_len))) { \
+ (ce)->destructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_DTOR; \
+ } else if (!strncmp((lcmname), ZEND_GET_FUNC_NAME, (mname_len))) { \
+ (ce)->__get = (fe); \
+ } else if (!strncmp((lcmname), ZEND_SET_FUNC_NAME, (mname_len))) { \
+ (ce)->__set = (fe); \
+ } else if (!strncmp((lcmname), ZEND_CALL_FUNC_NAME, (mname_len))) { \
+ (ce)->__call = (fe); \
+ } else if (!strncmp((lcmname), ZEND_UNSET_FUNC_NAME, (mname_len))) { \
+ (ce)->__unset = (fe); \
+ } else if (!strncmp((lcmname), ZEND_ISSET_FUNC_NAME, (mname_len))) { \
+ (ce)->__isset = (fe); \
+ } else if (!strncmp((lcmname), ZEND_CALLSTATIC_FUNC_NAME, (mname_len))) { \
+ (ce)->__callstatic = (fe); \
+ } else if (!strncmp((lcmname), ZEND_TOSTRING_FUNC_NAME, (mname_len))) { \
+ (ce)->__tostring = (fe); \
+ } else if ((ce)->name_length == (mname_len)) { \
+ char *lowercase_name = emalloc((ce)->name_length + 1); \
+ zend_str_tolower_copy(lowercase_name, (ce)->name, (ce)->name_length); \
+ if (!memcmp((lcmname), lowercase_name, (mname_len))) { \
+ if (!(ce)->constructor || (ce)->constructor == (orig_fe)) { \
+ (ce)->constructor = (fe); \
+ (fe)->common.fn_flags |= ZEND_ACC_CTOR; \
+ } \
+ } \
+ efree(lowercase_name); \
+ } \
}
-#define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe) { \
- if ((ce)->constructor == (fe)) (ce)->constructor = NULL; \
- else if ((ce)->destructor == (fe)) (ce)->destructor = NULL; \
- else if ((ce)->clone == (fe)) (ce)->clone = NULL; \
- else if ((ce)->__get == (fe)) (ce)->__get = NULL; \
- else if ((ce)->__set == (fe)) (ce)->__set = NULL; \
- else if ((ce)->__call == (fe)) (ce)->__call = NULL; \
+# define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe) { \
+ if ((ce)->constructor == (fe)) (ce)->constructor = NULL; \
+ else if ((ce)->destructor == (fe)) (ce)->destructor = NULL; \
+ else if ((ce)->__get == (fe)) (ce)->__get = NULL; \
+ else if ((ce)->__set == (fe)) (ce)->__set = NULL; \
+ else if ((ce)->__unset == (fe)) (ce)->__unset = NULL; \
+ else if ((ce)->__isset == (fe)) (ce)->__isset = NULL; \
+ else if ((ce)->__call == (fe)) (ce)->__call = NULL; \
+ else if ((ce)->__callstatic == (fe)) (ce)->__callstatic = NULL; \
+ else if ((ce)->__tostring == (fe)) (ce)->__tostring = NULL; \
+ else if ((ce)->clone == (fe)) (ce)->clone = NULL; \
+}
+# define PHP_RUNKIT_INHERIT_MAGIC(ce, fe, orig_fe, is_constr) { \
+ if ((ce)->__get == (orig_fe) && (ce)->parent->__get == (fe)) { \
+ (ce)->__get = (ce)->parent->__get; \
+ } else if ((ce)->__set == (orig_fe) && (ce)->parent->__set == (fe)) { \
+ (ce)->__set = (ce)->parent->__set; \
+ } else if ((ce)->__unset == (orig_fe) && (ce)->parent->__unset == (fe)) { \
+ (ce)->__unset = (ce)->parent->__unset; \
+ } else if ((ce)->__isset == (orig_fe) && (ce)->parent->__isset == (fe)) { \
+ (ce)->__isset = (ce)->parent->__isset; \
+ } else if ((ce)->__call == (orig_fe) && (ce)->parent->__call == (fe)) { \
+ (ce)->__call = (ce)->parent->__call; \
+ } else if ((ce)->__callstatic == (orig_fe) && (ce)->parent->__callstatic == (fe)) { \
+ (ce)->__callstatic = (ce)->parent->__callstatic; \
+ } else if ((ce)->__tostring == (orig_fe) && (ce)->parent->__tostring == (fe)) { \
+ (ce)->__tostring = (ce)->parent->__tostring; \
+ } else if ((ce)->clone == (orig_fe) && (ce)->parent->clone == (fe)) { \
+ (ce)->clone = (ce)->parent->clone; \
+ } else if ((ce)->destructor == (orig_fe) && (ce)->parent->destructor == (fe)) { \
+ (ce)->destructor = (ce)->parent->destructor; \
+ } else if ((ce)->constructor == (orig_fe) && (ce)->parent->constructor == (fe)) { \
+ (ce)->constructor = (ce)->parent->constructor; \
+ } \
+}
+#else
+# define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, lcmname, mname_len, fe, orig_fe) { \
+ if (!strncmp((lcmname), ZEND_CLONE_FUNC_NAME, (mname_len))) { \
+ (ce)->clone = (fe); (fe)->common.fn_flags |= ZEND_ACC_CLONE; \
+ } else if (!strncmp((lcmname), ZEND_CONSTRUCTOR_FUNC_NAME, (mname_len))) { \
+ if (!(ce)->constructor || (ce)->constructor == (orig_fe)) { \
+ (ce)->constructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_CTOR; \
+ } \
+ } else if (!strncmp((lcmname), ZEND_DESTRUCTOR_FUNC_NAME, (mname_len))) { \
+ (ce)->destructor = (fe); (fe)->common.fn_flags |= ZEND_ACC_DTOR; \
+ } else if (!strncmp((lcmname), ZEND_GET_FUNC_NAME, (mname_len))) { \
+ (ce)->__get = (fe); \
+ } else if (!strncmp((lcmname), ZEND_SET_FUNC_NAME, (mname_len))) { \
+ (ce)->__set = (fe); \
+ } else if (!strncmp((lcmname), ZEND_CALL_FUNC_NAME, (mname_len))) { \
+ (ce)->__call = (fe); \
+ } else if (!strncmp((lcmname), ZEND_UNSET_FUNC_NAME, (mname_len))) { \
+ (ce)->__unset = (fe); \
+ } else if (!strncmp((lcmname), ZEND_ISSET_FUNC_NAME, (mname_len))) { \
+ (ce)->__isset = (fe); \
+ } else if (!strncmp((lcmname), ZEND_TOSTRING_FUNC_NAME, (mname_len))) { \
+ (ce)->__tostring = (fe); \
+ } else if ((ce)->name_length == (mname_len)) { \
+ char *lowercase_name = emalloc((ce)->name_length + 1); \
+ zend_str_tolower_copy(lowercase_name, (ce)->name, (ce)->name_length); \
+ if (!memcmp((lcmname), lowercase_name, (mname_len))) { \
+ if (!(ce)->constructor || (ce)->constructor == (orig_fe)) { \
+ (ce)->constructor = (fe); \
+ (fe)->common.fn_flags |= ZEND_ACC_CTOR; \
+ } \
+ } \
+ efree(lowercase_name); \
+ } \
+}
+# define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe) { \
+ if ((ce)->constructor == (fe)) (ce)->constructor = NULL; \
+ else if ((ce)->destructor == (fe)) (ce)->destructor = NULL; \
+ else if ((ce)->__get == (fe)) (ce)->__get = NULL; \
+ else if ((ce)->__set == (fe)) (ce)->__set = NULL; \
+ else if ((ce)->__unset == (fe)) (ce)->__unset = NULL; \
+ else if ((ce)->__isset == (fe)) (ce)->__isset = NULL; \
+ else if ((ce)->__call == (fe)) (ce)->__call = NULL; \
+ else if ((ce)->__tostring == (fe)) (ce)->__tostring = NULL; \
+ else if ((ce)->clone == (fe)) (ce)->clone = NULL; \
}
+# define PHP_RUNKIT_INHERIT_MAGIC(ce, fe, orig_fe, is_constr) { \
+ if ((ce)->__get == (orig_fe) && (ce)->parent->__get == (fe)) { \
+ (ce)->__get = (ce)->parent->__get; \
+ } else if ((ce)->__set == (orig_fe) && (ce)->parent->__set == (fe)) { \
+ (ce)->__set = (ce)->parent->__set; \
+ } else if ((ce)->__unset == (orig_fe) && (ce)->parent->__unset == (fe)) { \
+ (ce)->__unset = (ce)->parent->__unset; \
+ } else if ((ce)->__isset == (orig_fe) && (ce)->parent->__isset == (fe)) { \
+ (ce)->__isset = (ce)->parent->__isset; \
+ } else if ((ce)->__call == (orig_fe) && (ce)->parent->__call == (fe)) { \
+ (ce)->__call = (ce)->parent->__call; \
+ } else if ((ce)->__tostring == (orig_fe) && (ce)->parent->__tostring == (fe)) { \
+ (ce)->__tostring = (ce)->parent->__tostring; \
+ } else if ((ce)->clone == (orig_fe) && (ce)->parent->clone == (fe)) { \
+ (ce)->clone = (ce)->parent->clone; \
+ } else if ((ce)->destructor == (orig_fe) && (ce)->parent->destructor == (fe)) { \
+ (ce)->destructor = (ce)->parent->destructor; \
+ } else if ((ce)->constructor == (orig_fe) && (ce)->parent->constructor == (fe)) { \
+ (ce)->constructor = (ce)->parent->constructor; \
+ } \
+}
+#endif // RUNKIT_ABOVE53
#define PHP_RUNKIT_DESTROY_FUNCTION(fe) destroy_zend_function(fe TSRMLS_CC);
#else
#define PHP_RUNKIT_DESTROY_FUNCTION(fe) destroy_zend_function(fe);
-#define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, method, fe)
-#define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe)
+#define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, lcmname, mname_len, fe, orig_fe)
+#define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe) { \
+ zend_function *current_constr; \
+ if (zend_hash_find(&(ce)->function_table, (ce)->name, (ce)->name_length + 1, (void **) &current_constr) == SUCCESS && \
+ current_constr && current_constr->op_array.opcodes == (fe)->op_array.opcodes && (fe) != current_constr) { \
+ zend_hash_del(&ce->function_table, ce->name, ce->name_length+1); \
+ } \
+}
+#define PHP_RUNKIT_INHERIT_MAGIC(ce, fe, orig_fe, is_constr) { \
+ zend_function *current_constr = NULL; \
+ if (is_constr) { \
+ zend_hash_find(&(ce)->function_table, (ce)->name, (ce)->name_length + 1, (void **) &current_constr); \
+ } \
+ if (((orig_fe) && current_constr && current_constr->op_array.opcodes == (orig_fe)->op_array.opcodes) || \
+ (!(orig_fe) && (is_constr) && !current_constr) \
+ ) { \
+ zend_hash_update(&ce->function_table, ce->name, ce->name_length + 1, (fe), sizeof(zend_function), NULL); \
+ function_add_ref(fe); \
+ } \
+}
#endif /* ZEND_ENGINE_2 */
#endif /* PHP_RUNKIT_MANIPULATION */
View
43 runkit_classes.c
@@ -33,8 +33,8 @@ static int php_runkit_remove_inherited_methods(zend_function *fe, zend_class_ent
return ZEND_HASH_APPLY_KEEP;
}
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 4, ancestor_class, ce, function_name, function_name_len);
-
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 5,
+ ancestor_class, ce, function_name, function_name_len, fe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe);
return ZEND_HASH_APPLY_REMOVE;
@@ -62,7 +62,12 @@ PHP_FUNCTION(runkit_class_emancipate)
RETURN_TRUE;
}
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
+ php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
+#endif
+
zend_hash_apply_with_argument(&ce->function_table, (apply_func_arg_t)php_runkit_remove_inherited_methods, ce TSRMLS_CC);
+ ce->parent = NULL;
RETURN_TRUE;
}
@@ -81,21 +86,37 @@ static int php_runkit_inherit_methods(zend_function *fe, zend_class_entry *ce TS
return ZEND_HASH_APPLY_KEEP;
}
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5, ancestor_class, ce, fe, function_name, function_name_len);
-
- PHP_RUNKIT_FUNCTION_ADD_REF(fe);
-
/* method name keys must be lower case */
lower_function_name = estrndup(function_name, function_name_len);
php_strtolower(lower_function_name, function_name_len);
+
+#if PHP_MAJOR_VERSION < 5
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, fe, function_name, function_name_len, NULL,
+ ce->name_length == function_name_len && !strncmp(ce->name, lower_function_name, ce->name_length));
+#endif
+
if (zend_hash_add_or_update(&ce->function_table, lower_function_name, function_name_len + 1, fe, sizeof(zend_function), NULL, HASH_ADD) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error inheriting parent method: %s()", fe->common.function_name);
efree(lower_function_name);
return ZEND_HASH_APPLY_KEEP;
}
- efree(lower_function_name);
- PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fe->common.function_name, fe);
+ if (zend_hash_find(&ce->function_table, lower_function_name, function_name_len + 1, (void**)&fe) == FAILURE ||
+ !fe) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate newly added method");
+ efree(lower_function_name);
+ return ZEND_HASH_APPLY_KEEP;
+ }
+
+ PHP_RUNKIT_FUNCTION_ADD_REF(fe);
+ PHP_RUNKIT_ADD_MAGIC_METHOD(ce, lower_function_name, function_name_len, fe, NULL);
+
+#if PHP_MAJOR_VERSION >= 5
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, fe, function_name, function_name_len, NULL, 0);
+#endif
+ efree(lower_function_name);
return ZEND_HASH_APPLY_KEEP;
}
@@ -126,6 +147,12 @@ PHP_FUNCTION(runkit_class_adopt)
RETURN_FALSE;
}
+ ce->parent = parent;
+
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
+ php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
+#endif
+
zend_hash_apply_with_argument(&parent->function_table, (apply_func_arg_t)php_runkit_inherit_methods, ce TSRMLS_CC);
RETURN_TRUE;
View
20 runkit_import.c
@@ -117,7 +117,7 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
zend_hash_internal_pointer_reset_ex(&ce->function_table, &pos);
while (zend_hash_get_current_data_ex(&ce->function_table, (void**)&fe, &pos) == SUCCESS) {
- zend_function *dfe;
+ zend_function *dfe = NULL;
int fn_len = strlen(fe->common.function_name);
zend_class_entry *fe_scope = php_runkit_locate_scope(ce, fe, fe->common.function_name, fn_len);
@@ -134,6 +134,7 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
memcpy(fn, fe->common.function_name, fn_len + 1);
php_strtolower(fn, fn_len);
+ dfe = NULL;
if (zend_hash_find(&dce->function_table, fn, fn_len + 1, (void**)&dfe) == SUCCESS) {
zend_class_entry *scope;
if (!override) {
@@ -154,7 +155,7 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
*clear_cache = 1;
#endif
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 4, scope, dce, fn, fn_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 5, scope, dce, fn, fn_len, dfe);
if (zend_hash_del(&dce->function_table, fn, fn_len + 1) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error removing old method in destination class %s::%s", dce->name, fe->common.function_name);
zend_hash_move_forward_ex(&ce->function_table, &pos);
@@ -173,7 +174,20 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
PHP_RUNKIT_FUNCTION_ADD_REF(fe);
}
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5, dce, dce, fe, fn, fn_len);
+ if (zend_hash_find(&dce->function_table, fn, fn_len + 1, (void **)&fe) != SUCCESS) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot get newly created method %s::%s()", ce->name, fn);
+ zend_hash_move_forward_ex(&ce->function_table, &pos);
+ continue;
+ }
+ PHP_RUNKIT_ADD_MAGIC_METHOD(dce, fn, fn_len, fe, dfe);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ dce, dce, fe, fn, fn_len, dfe,
+#ifdef ZEND_ENGINE_2
+ 0
+#else
+ !strncmp(ce->name, fe->common.function_name, ce->name_length)
+#endif
+ );
zend_hash_move_forward_ex(&ce->function_table, &pos);
}
View
78 runkit_methods.c
@@ -270,6 +270,10 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
zend_function *fe = va_arg(args, zend_function*);
char *fname = va_arg(args, char*);
int fname_len = va_arg(args, int);
+ zend_function *orig_fe = va_arg(args, zend_function*);
+#ifndef ZEND_ENGINE_2
+ int is_constr = va_arg(args, int);
+#endif
zend_function *cfe = NULL;
char *fname_lower;
RUNKIT_UNDER53_TSRMLS_FETCH();
@@ -299,6 +303,14 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
}
}
+#ifndef ZEND_ENGINE_2
+ /* Process children of this child */
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, fe, fname, fname_len, orig_fe,
+ is_constr || (fname_len == ce->name_length && !strncmp(ce->name, fname_lower, ce->name_length))
+ );
+#endif
+
if (cfe && zend_hash_del(&ce->function_table, fname_lower, fname_len + 1) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error updating child class");
efree(fname_lower);
@@ -311,11 +323,13 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
return ZEND_HASH_APPLY_KEEP;
}
PHP_RUNKIT_FUNCTION_ADD_REF(fe);
+ PHP_RUNKIT_INHERIT_MAGIC(ce, fe, orig_fe, is_constr);
- PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fname, fe);
-
+#ifdef ZEND_ENGINE_2
/* Process children of this child */
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5, ancestor_class, ce, fe, fname, fname_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, fe, fname, fname_len, orig_fe, 0);
+#endif
efree(fname_lower);
@@ -332,6 +346,7 @@ int php_runkit_clean_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce)
zend_class_entry *scope;
char *fname = va_arg(args, char*);
int fname_len = va_arg(args, int);
+ zend_function *orig_cfe = va_arg(args, zend_function *);
zend_function *cfe = NULL;
char *fname_lower;
RUNKIT_UNDER53_TSRMLS_FETCH();
@@ -368,11 +383,11 @@ int php_runkit_clean_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce)
}
/* Process children of this child */
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 4, ancestor_class, ce, fname, fname_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 5, ancestor_class, ce, fname, fname_len, orig_cfe);
zend_hash_del(&ce->function_table, fname_lower, fname_len + 1);
- PHP_RUNKIT_DEL_MAGIC_METHOD(ce, cfe);
+ PHP_RUNKIT_DEL_MAGIC_METHOD(ce, orig_cfe);
efree(fname_lower);
return ZEND_HASH_APPLY_KEEP;
@@ -386,7 +401,7 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
const char *classname, *methodname, *arguments, *phpcode;
int classname_len, methodname_len, arguments_len, phpcode_len;
zend_class_entry *ce, *ancestor_class = NULL;
- zend_function func, *fe;
+ zend_function func, *fe, *orig_fe = NULL;
char *methodname_lower;
long argc = ZEND_NUM_ARGS();
#ifdef ZEND_ENGINE_2
@@ -428,6 +443,7 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
RETURN_FALSE;
}
ancestor_class = php_runkit_locate_scope(ce, fe, methodname, methodname_len);
+ orig_fe = fe;
if (php_runkit_check_call_stack(&fe->op_array TSRMLS_CC) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot redefine a method while that method is active.");
@@ -477,11 +493,13 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
#endif
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5,
- ancestor_class, ce, &func, methodname, methodname_len);
-
+#if PHP_MAJOR_VERSION < 5
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, &func, methodname, methodname_len, orig_fe,
+ methodname_len == ce->name_length && !strncmp(ce->name, methodname_lower, ce->name_length));
+#endif
- if (zend_hash_add_or_update(&ce->function_table, methodname_lower, methodname_len + 1, &func, sizeof(zend_op_array), NULL, add_or_update) == FAILURE) {
+ if (zend_hash_add_or_update(&ce->function_table, methodname_lower, methodname_len + 1, &func, sizeof(zend_function), NULL, add_or_update) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add method to class");
efree(methodname_lower);
RETURN_FALSE;
@@ -500,7 +518,12 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
RETURN_FALSE;
}
- PHP_RUNKIT_ADD_MAGIC_METHOD(ce, methodname, fe);
+ PHP_RUNKIT_ADD_MAGIC_METHOD(ce, methodname_lower, methodname_len, fe, orig_fe);
+#if PHP_MAJOR_VERSION >= 5
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ancestor_class, ce, fe, methodname, methodname_len, orig_fe, 0);
+#endif
+
efree(methodname_lower);
RETURN_TRUE;
@@ -551,9 +574,16 @@ static int php_runkit_method_copy(const char *dclass, int dclass_len, const char
return FAILURE;
}
- PHP_RUNKIT_ADD_MAGIC_METHOD(dce, dfunc, dfeInHashTable);
+ PHP_RUNKIT_ADD_MAGIC_METHOD(dce, dfunc_lower, dfunc_len, dfeInHashTable, NULL);
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5, dce, dce, &dfe, dfunc_lower, dfunc_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ dce, dce, dfeInHashTable, dfunc_lower, dfunc_len, NULL,
+#ifdef ZEND_ENGINE_2
+ 0
+#else
+ dfunc_len == dce->name_length && !strncmp(dce->name, dfunc_lower, dce->name_length)
+#endif
+ );
efree(dfunc_lower);
return SUCCESS;
@@ -615,7 +645,7 @@ PHP_FUNCTION(runkit_method_remove)
}
php_strtolower(methodname_lower, methodname_len);
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 4, ancestor_class, ce, methodname, methodname_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 5, ancestor_class, ce, methodname, methodname_len, fe);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
@@ -645,8 +675,7 @@ PHP_FUNCTION(runkit_method_rename)
char *newname_lower, *methodname_lower;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s/s/s/", &classname, &classname_len,
- &methodname, &methodname_len,
- &newname, &newname_len) == FAILURE) {
+ &methodname, &methodname_len, &newname, &newname_len) == FAILURE) {
RETURN_FALSE;
}
@@ -682,8 +711,8 @@ PHP_FUNCTION(runkit_method_rename)
}
ancestor_class = php_runkit_locate_scope(ce, fe, methodname_lower, methodname_len);
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 4,
- ancestor_class, ce, methodname_lower, methodname_len);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_clean_children_methods, 5,
+ ancestor_class, ce, methodname_lower, methodname_len, fe);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
@@ -718,11 +747,18 @@ PHP_FUNCTION(runkit_method_rename)
RETURN_FALSE;
}
+ PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname_lower, newname_len, fe, NULL);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 7,
+ ce, ce, fe, newname, newname_len, NULL,
+#ifdef ZEND_ENGINE_2
+ 0
+#else
+ newname_len == ce->name_length && !strncmp(ce->name, newname_lower, ce->name_length)
+#endif
+ );
+
efree(newname_lower);
efree(methodname_lower);
- PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname, fe);
-
- zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 5, ce, ce, fe, newname, newname_len);
RETURN_TRUE;
}
View
124 tests/runkit_add_magic_methods.phpt
@@ -0,0 +1,124 @@
+--TEST--
+adding and removing magic methods
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip";
+ if(array_shift(explode('.', PHP_VERSION)) < 5) print "skip";
+?>
+--FILE--
+<?php
+
+class Test {
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_Child extends FOO_test {
+}
+
+runkit_method_add("Test", "__construct", "", 'echo "__construct\n";');
+runkit_method_add("Test", "__destruct", "", 'echo "__destruct\n";');
+runkit_method_add("Test", "__get", "", 'echo "__get\n";');
+runkit_method_add("Test", "__set", "", 'echo "__set\n";');
+runkit_method_add("Test", "__call", "", 'echo "__call\n";');
+runkit_method_add("Test", "__unset", "", 'echo "__unset\n";');
+runkit_method_add("Test", "__isset", "", 'echo "__isset\n";');
+runkit_method_add("Test", "__callStatic", "", 'echo "__callstatic\n";', RUNKIT_ACC_STATIC);
+$a = new test;
+$b = new foo_test;
+$c = new FOO_test_Child;
+$a->test;
+$b->test;
+$c->test;
+$a->test = 1;
+$b->test = 2;
+$c->test = 3;
+$a->method();
+$b->method();
+$c->method();
+unset($a->test);
+unset($b->test);
+unset($c->test);
+isset($a->test);
+isset($b->test);
+isset($c->test);
+$a = NULL;
+$b = NULL;
+$c = NULL;
+if(version_compare(PHP_VERSION, '5.2.999', '>')) {
+ Test::method();
+ FOO_Test::method();
+ FOO_Test_child::method();
+} else {
+ echo "__callstatic\n";
+ echo "__callstatic\n";
+ echo "__callstatic\n";
+}
+
+runkit_method_remove("Test", "__construct");
+runkit_method_remove("Test", "__destruct");
+runkit_method_remove("Test", "__get");
+runkit_method_remove("Test", "__set");
+runkit_method_remove("Test", "__call");
+runkit_method_remove("Test", "__unset");
+runkit_method_remove("Test", "__isset");
+runkit_method_remove("Test", "__callstatic");
+echo "after removing\n";
+
+$a = new test;
+$b = new foo_test;
+$c = new FOO_test_Child;
+$a->test;
+$b->test;
+$c->test;
+$a->test = 1;
+$b->test = 2;
+$c->test = 3;
+$a->method();
+$b->method();
+$c->method();
+unset($a->test);
+unset($b->test);
+unset($c->test);
+isset($a->test);
+isset($b->test);
+isset($c->test);
+$a = NULL;
+$b = NULL;
+$c = NULL;
+FOO_Test_child::method();
+?>
+--EXPECTF--
+__construct
+__construct
+__construct
+__get
+__get
+__get
+__set
+__set
+__set
+__call
+__call
+__call
+__unset
+__unset
+__unset
+__isset
+__isset
+__isset
+__destruct
+__destruct
+__destruct
+__callstatic
+__callstatic
+__callstatic
+after removing
+
+Notice: Undefined property: %s in %s on line %d
+
+Notice: Undefined property: %s in %s on line %d
+
+Notice: Undefined property: %s in %s on line %d
+
+Fatal error: Call to undefined method %s in %s on line %d
View
36 tests/runkit_add_old_style_ctor.phpt
@@ -0,0 +1,36 @@
+--TEST--
+add old-style parent ctor
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Test {
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_Child extends FOO_test {
+}
+
+runkit_method_add("test", "test", "", "var_dump('new constructor');");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+echo "after removing\n";
+
+runkit_method_remove("test", "test");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+echo "==DONE==\n";
+?>
+--EXPECT--
+string(15) "new constructor"
+string(15) "new constructor"
+string(15) "new constructor"
+after removing
+==DONE==
View
56 tests/runkit_add_old_style_ctor1.phpt
@@ -0,0 +1,56 @@
+--TEST--
+add old-style parent ctor (existing ctor)
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Test {
+}
+
+class FOO_test extends test {
+ function foo_test() {
+ var_dump("foo_test ctor");
+ }
+}
+
+class Foo_test_Child extends FOO_test{
+}
+
+class Test_Child extends Test{
+}
+
+class Test_GrandChild extends Test_Child {
+ function test_grandchild() {
+ var_dump("test_grandchild ctor");
+ }
+}
+
+runkit_method_add("test", "test", "", "var_dump('new constructor');");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+$a = new Test_Child;
+$a = new Test_GrandChild;
+
+echo "after removing\n";
+runkit_method_remove("test", "test");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+$a = new Test_Child;
+$a = new Test_GrandChild;
+
+echo "==DONE==\n";
+?>
+--EXPECT--
+string(15) "new constructor"
+string(13) "foo_test ctor"
+string(13) "foo_test ctor"
+string(15) "new constructor"
+string(20) "test_grandchild ctor"
+after removing
+string(13) "foo_test ctor"
+string(13) "foo_test ctor"
+string(20) "test_grandchild ctor"
+==DONE==
View
39 tests/runkit_add_old_style_ctor_by_adopting.phpt
@@ -0,0 +1,39 @@
+--TEST--
+add old-style parent ctor by adoptting
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Father {
+ function teSt() {
+ var_dump('new constructor');
+ }
+}
+
+class Test {
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_Child extends FOO_test {
+}
+
+runkit_class_adopt("Test", "Father");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+runkit_class_emancipate("Test");
+echo "after emancipation\n";
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+?>
+--EXPECT--
+string(15) "new constructor"
+string(15) "new constructor"
+string(15) "new constructor"
+after emancipation
View
39 tests/runkit_add_old_style_ctor_by_copying.phpt
@@ -0,0 +1,39 @@
+--TEST--
+add old-style parent ctor by copying
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Test {
+ function test1() {
+ var_dump('new constructor');
+ }
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_Child extends FOO_test {
+}
+
+runkit_method_copy("test", "test", "test", "test1");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+echo "after removing\n";
+
+runkit_method_remove("test", "test");
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+echo "==DONE==\n";
+?>
+--EXPECT--
+string(15) "new constructor"
+string(15) "new constructor"
+string(15) "new constructor"
+after removing
+==DONE==
View
7 tests/runkit_add_old_style_ctor_by_importing.inc
@@ -0,0 +1,7 @@
+<?php
+
+class Test {
+ function test() {
+ var_dump('new constructor');
+ }
+}
View
28 tests/runkit_add_old_style_ctor_by_importing.phpt
@@ -0,0 +1,28 @@
+--TEST--
+add old-style parent ctor by importing
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Test {
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_Child extends FOO_test {
+}
+
+runkit_import("runkit_add_old_style_ctor_by_importing.inc", RUNKIT_IMPORT_CLASSES);
+$a = new test;
+$a = new foo_test;
+$a = new FOO_test_Child;
+
+echo "==DONE==\n";
+?>
+--EXPECT--
+string(15) "new constructor"
+string(15) "new constructor"
+string(15) "new constructor"
+==DONE==
View
22 tests/runkit_adopt_emancipate_and_inheritance.phpt
@@ -0,0 +1,22 @@
+--TEST--
+runkit_class_adopt, runkit_class_emancipate and inheritance
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+class myParent {}
+class myUncle {}
+class myChild extends myParent {}
+
+echo get_parent_class("myChild"), "\n";
+
+runkit_class_emancipate("myChild");
+echo get_parent_class("myChild") . "\n";
+
+runkit_class_adopt("myChild", "myUncle");
+echo get_parent_class("myChild") . "\n";
+
+--EXPECTF--
+my%sarent
+
+my%sncle
View
60 tests/runkit_redefine_old_style_ctor.phpt
@@ -0,0 +1,60 @@
+--TEST--
+redefine old-style parent ctor
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip"; ?>
+--FILE--
+<?php
+
+class Test {
+ function test() {
+ var_dump("original constructor");
+ }
+}
+
+class FOO_test extends test {
+}
+
+class FOO_test_child extends FOO_test {
+}
+
+class FOO_test_child_changed extends Foo_test_child {
+ function Foo_test_child_changed() {
+ var_dump("FOO_test_child_changed constructor");
+ }
+}
+
+class FOO_test_child_changed_child extends FOO_test_child_changed {
+}
+
+runkit_method_redefine("test", "test", "", "var_dump('new constructor');");
+$a = new test;
+$a = new foo_test;
+$a = new foo_test_child;
+$a = new foo_test_child_changed;
+$a = new foo_test_child_changed_child;
+
+echo "after renaming\n";
+runkit_method_rename("test", "test", "test1");
+$a = new test;
+$a = new foo_test;
+$a = new foo_test_child;
+$a = new foo_test_child_changed;
+$a = new foo_test_child_changed_child;
+
+echo "\n";
+$a = new foo_test;
+$a->test1();
+echo "==DONE==\n";
+?>
+--EXPECT--
+string(15) "new constructor"
+string(15) "new constructor"
+string(15) "new constructor"
+string(34) "FOO_test_child_changed constructor"
+string(34) "FOO_test_child_changed constructor"
+after renaming
+string(34) "FOO_test_child_changed constructor"
+string(34) "FOO_test_child_changed constructor"
+
+string(15) "new constructor"
+==DONE==

0 comments on commit 7f5b7b1

Please sign in to comment.