Permalink
Browse files

Merge branch 'update_children_method_prototype' of https://github.com…

…/tony2001/runkit

Conflicts:
	runkit_methods.c

+ small fixes
+ a little bit of refactoring and optimization
  • Loading branch information...
zenovich committed Sep 19, 2012
2 parents d39962b + 510d34b commit a978619ed9684b7f7b0bd983ffcd5fdbe400a491
View
@@ -53,6 +53,8 @@ Execute code in restricted environment (sandboxing).
* Copying of functions was reworked
* Properties adding, removing and importing were corrected for right inheritance
* Converting of input parameters (class names & function names) to lowecase was eliminated
+ * runkit_method_redefine function now sets the 'prototype' field for the method
+ and its descendants (Thanks to Anthony Dovgal).
* Test for correctness of runkit.superglobals feature was added
* Tests for correctness of inheritance of properties were added
* Tests for correctness of adding static properties were added
@@ -116,6 +118,8 @@ Execute code in restricted environment (sandboxing).
<file name="runkit_method_copy.phpt" role="test" />
<file name="runkit_method_redefine.phpt" role="test" />
<file name="runkit_method_redefine_protected.phpt" role="test" />
+ <file name="runkit_method_redefine_update_proto.phpt" role="test" />
+ <file name="runkit_method_redefine_update_proto2.phpt" role="test" />
<file name="runkit_method_redefine_with_static_vars.phpt" role="test" />
<file name="runkit_method_remove.phpt" role="test" />
<file name="runkit_method_rename.phpt" role="test" />
View
@@ -208,7 +208,7 @@ int php_runkit_fetch_interface(const char *classname, int classname_len, zend_cl
#if PHP_MAJOR_VERSION >= 6
#define PHP_RUNKIT_FUNCTION_ADD_REF(f) function_add_ref(f TSRMLS_CC)
-#define php_runkit_locate_scope(ce, fe, methodname, methodname_len) fe->common.scope
+#define php_runkit_locate_scope(ce, fe, methodname_lower, methodname_len) fe->common.scope
#define PHP_RUNKIT_DECL_STRING_PARAM(param) void *param; int32_t param##_len; zend_uchar param##_type;
#define PHP_RUNKIT_STRING_SPEC "t"
#define PHP_RUNKIT_STRING_PARAM(param) &param, &param##_len, &param##_type
@@ -222,7 +222,7 @@ int php_runkit_fetch_interface(const char *classname, int classname_len, zend_cl
#elif PHP_MAJOR_VERSION >= 5
#define PHP_RUNKIT_FUNCTION_ADD_REF(f) function_add_ref(f)
-#define php_runkit_locate_scope(ce, fe, methodname, methodname_len) fe->common.scope
+#define php_runkit_locate_scope(ce, fe, methodname_lower, methodname_len) fe->common.scope
#define PHP_RUNKIT_DECL_STRING_PARAM(p) char *p; int p##_len;
#define PHP_RUNKIT_STRING_SPEC "s"
#define PHP_RUNKIT_STRING_PARAM(p) &p, &p##_len
@@ -236,8 +236,8 @@ int php_runkit_fetch_interface(const char *classname, int classname_len, zend_cl
#else /* PHP4 */
#define PHP_RUNKIT_FUNCTION_ADD_REF(f) function_add_ref(f)
-zend_class_entry *_php_runkit_locate_scope(zend_class_entry *ce, zend_function *fe, const char *methodname, int methodname_len TSRMLS_DC);
-#define php_runkit_locate_scope(ce, fe, methodname, methodname_len) _php_runkit_locate_scope((ce), (fe), (methodname), (methodname_len) TSRMLS_CC)
+zend_class_entry *_php_runkit_locate_scope(zend_class_entry *ce, zend_function *fe, const char *methodname_lower, int methodname_len TSRMLS_DC);
+#define php_runkit_locate_scope(ce, fe, methodname_lower, methodname_len) _php_runkit_locate_scope((ce), (fe), (methodname_lower), (methodname_len) TSRMLS_CC)
#define PHP_RUNKIT_DECL_STRING_PARAM(p) char *p; int p##_len;
#define PHP_RUNKIT_STRING_SPEC "s"
#define PHP_RUNKIT_STRING_PARAM(p) &p, &p##_len
View
@@ -27,7 +27,19 @@ static int php_runkit_remove_inherited_methods(zend_function *fe, zend_class_ent
{
const char *function_name = fe->common.function_name;
int function_name_len = strlen(function_name);
- zend_class_entry *ancestor_class = php_runkit_locate_scope(ce, fe, function_name, function_name_len);
+ char *fname_lower;
+ zend_class_entry *ancestor_class;
+
+ fname_lower = estrndup(function_name, function_name_len);
+ if (fname_lower == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
+ return ZEND_HASH_APPLY_KEEP;
+ }
+ php_strtolower(fname_lower, function_name_len);
+
+ ancestor_class = php_runkit_locate_scope(ce, fe, fname_lower, function_name_len);
+
+ efree(fname_lower);
if (ancestor_class == ce) {
return ZEND_HASH_APPLY_KEEP;
@@ -80,16 +92,23 @@ static int php_runkit_inherit_methods(zend_function *fe, zend_class_entry *ce TS
const char *function_name = fe->common.function_name;
char *lower_function_name;
int function_name_len = strlen(function_name);
- zend_class_entry *ancestor_class = php_runkit_locate_scope(ce, fe, function_name, function_name_len);
-
- if (zend_hash_exists(&ce->function_table, (char *) function_name, function_name_len + 1)) {
- return ZEND_HASH_APPLY_KEEP;
- }
+ zend_class_entry *ancestor_class;
/* method name keys must be lower case */
lower_function_name = estrndup(function_name, function_name_len);
+ if (lower_function_name == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
+ return ZEND_HASH_APPLY_KEEP;
+ }
php_strtolower(lower_function_name, function_name_len);
+ if (zend_hash_exists(&ce->function_table, (char *) lower_function_name, function_name_len + 1)) {
+ efree(lower_function_name);
+ return ZEND_HASH_APPLY_KEEP;
+ }
+
+ ancestor_class = php_runkit_locate_scope(ce, fe, 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,
@@ -113,8 +132,8 @@ static int php_runkit_inherit_methods(zend_function *fe, zend_class_entry *ce TS
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);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 6,
+ ancestor_class, ce, fe, function_name, function_name_len, NULL);
#endif
efree(lower_function_name);
View
@@ -119,13 +119,7 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
while (zend_hash_get_current_data_ex(&ce->function_table, (void*)&fe, &pos) == SUCCESS) {
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);
-
- if (fe_scope != ce) {
- /* This is an inhereted function, let's skip it */
- zend_hash_move_forward_ex(&ce->function_table, &pos);
- continue;
- }
+ zend_class_entry *fe_scope;
if (fn_len > fn_maxlen - 1) {
fn_maxlen = fn_len + 33;
@@ -134,6 +128,14 @@ 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);
+ fe_scope = php_runkit_locate_scope(ce, fe, fn, fn_len);
+
+ if (fe_scope != ce) {
+ /* This is an inhereted function, let's skip it */
+ zend_hash_move_forward_ex(&ce->function_table, &pos);
+ continue;
+ }
+
dfe = NULL;
if (zend_hash_find(&dce->function_table, fn, fn_len + 1, (void*)&dfe) == SUCCESS) {
zend_class_entry *scope;
View
@@ -26,28 +26,18 @@
#ifndef ZEND_ENGINE_2
/* {{{ _php_runkit_locate_scope
ZendEngine 1 hack to determine a function's scope */
-zend_class_entry *_php_runkit_locate_scope(zend_class_entry *ce, zend_function *fe, const char *methodname, int methodname_len TSRMLS_DC)
+zend_class_entry *_php_runkit_locate_scope(zend_class_entry *ce, zend_function *fe, const char *methodname_lower, int methodname_len TSRMLS_DC)
{
zend_class_entry *current = ce->parent, *top = ce;
zend_function *func;
- char *methodname_lower;
-
- methodname_lower = estrndup(methodname, methodname_len);
- if (methodname_lower == NULL) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
- return top;
- }
- php_strtolower(methodname_lower, methodname_len);
while (current) {
- if (zend_hash_find(&current->function_table, methodname_lower, methodname_len + 1, (void*)&func) == FAILURE) {
+ if (zend_hash_find(&current->function_table, (char *) methodname_lower, methodname_len + 1, (void*)&func) == FAILURE) {
/* Not defined at this point (or higher) */
- efree(methodname_lower);
return top;
}
if (func->op_array.opcodes != fe->op_array.opcodes) {
/* Different function above this point */
- efree(methodname_lower);
return top;
}
/* Same opcodes */
@@ -56,35 +46,25 @@ zend_class_entry *_php_runkit_locate_scope(zend_class_entry *ce, zend_function *
current = current->parent;
}
- efree(methodname_lower);
return top;
}
/* }}} */
#else
/* {{{ _php_runkit_get_method_prototype
Locates the prototype method */
-zend_function* _php_runkit_get_method_prototype(zend_class_entry *ce, const char* func, int func_len TSRMLS_DC) {
+static inline zend_function* _php_runkit_get_method_prototype(zend_class_entry *ce, const char* func_lower, int func_len TSRMLS_DC) {
zend_class_entry *pce = ce;
zend_function *proto = NULL;
- char *func_lower;
- func_lower = estrndup(func, func_len);
- if (func_lower == NULL) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
- return NULL;
- }
-
- php_strtolower(func_lower, func_len);
while (pce) {
- if (zend_hash_find(&pce->function_table, func_lower, func_len+1, (void*) &proto) != FAILURE) {
+ if (zend_hash_find(&pce->function_table, (char *) func_lower, func_len+1, (void*) &proto) != FAILURE) {
break;
}
pce = pce->parent;
}
if (!pce) {
proto = NULL;
}
- efree(func_lower);
return proto;
}
/* }}} */
@@ -276,28 +256,33 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
#endif
zend_function *cfe = NULL;
char *fname_lower;
+
RUNKIT_UNDER53_TSRMLS_FETCH();
- fname_lower = estrndup(fname, fname_len);
- if (fname_lower == NULL) {
- php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
- return FAILURE;
- }
- php_strtolower(fname_lower, fname_len);
#ifdef ZEND_ENGINE_2
ce = *((zend_class_entry**)ce);
#endif
if (ce->parent != parent_class) {
/* Not a child, ignore */
- efree(fname_lower);
return ZEND_HASH_APPLY_KEEP;
}
+ fname_lower = estrndup(fname, fname_len);
+ if (fname_lower == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
+ return FAILURE;
+ }
+ php_strtolower(fname_lower, fname_len);
+
if (zend_hash_find(&ce->function_table, fname_lower, fname_len + 1, (void*)&cfe) == SUCCESS) {
- scope = php_runkit_locate_scope(ce, cfe, fname, fname_len);
+ scope = php_runkit_locate_scope(ce, cfe, fname_lower, fname_len);
if (scope != ancestor_class) {
/* This method was defined below our current level, leave it be */
+#ifdef ZEND_ENGINE_2
+ cfe->common.prototype = _php_runkit_get_method_prototype(scope->parent, fname_lower, fname_len TSRMLS_CC);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 6, ancestor_class, ce, fe, fname, fname_len, orig_fe);
+#endif
efree(fname_lower);
return ZEND_HASH_APPLY_KEEP;
}
@@ -327,8 +312,8 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
#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, 7,
- ancestor_class, ce, fe, fname, fname_len, orig_fe, 0);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 6,
+ ancestor_class, ce, fe, fname, fname_len, orig_fe);
#endif
efree(fname_lower);
@@ -368,7 +353,7 @@ int php_runkit_clean_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce)
}
if (zend_hash_find(&ce->function_table, fname_lower, fname_len + 1, (void*)&cfe) == SUCCESS) {
- scope = php_runkit_locate_scope(ce, cfe, fname, fname_len);
+ scope = php_runkit_locate_scope(ce, cfe, fname_lower, fname_len);
if (scope != ancestor_class) {
/* This method was defined below our current level, leave it be */
efree(fname_lower);
@@ -436,7 +421,7 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
efree(methodname_lower);
RETURN_FALSE;
}
- ancestor_class = php_runkit_locate_scope(ce, fe, methodname, methodname_len);
+ ancestor_class = php_runkit_locate_scope(ce, fe, methodname_lower, methodname_len);
orig_fe = fe;
if (php_runkit_check_call_stack(&fe->op_array TSRMLS_CC) == FAILURE) {
@@ -464,9 +449,6 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
efree((void*)func.common.function_name);
func.common.function_name = estrndup(methodname, methodname_len);
#ifdef ZEND_ENGINE_2
- func.common.scope = ce;
- func.common.prototype = _php_runkit_get_method_prototype(ce, methodname, methodname_len TSRMLS_CC);
-
if (flags & ZEND_ACC_PRIVATE) {
func.common.fn_flags &= ~ZEND_ACC_PPP_MASK;
func.common.fn_flags |= ZEND_ACC_PRIVATE;
@@ -514,10 +496,15 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
RETURN_FALSE;
}
+#ifdef ZEND_ENGINE_2
+ fe->common.scope = ce;
+ fe->common.prototype = _php_runkit_get_method_prototype(ce->parent, methodname_lower, methodname_len TSRMLS_CC);
+#endif
+
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);
+ zend_hash_apply_with_arguments(RUNKIT_53_TSRMLS_PARAM(EG(class_table)), (apply_func_args_t)php_runkit_update_children_methods, 6,
+ ancestor_class, ce, fe, methodname, methodname_len, orig_fe);
#endif
efree(methodname_lower);
@@ -559,17 +546,17 @@ static int php_runkit_method_copy(const char *dclass, int dclass_len, const char
dfe = *sfe;
php_runkit_function_copy_ctor(&dfe, dfunc, dfunc_len TSRMLS_CC);
-#ifdef ZEND_ENGINE_2
- dfe.common.scope = dce;
- dfe.common.prototype = _php_runkit_get_method_prototype(dce, dfunc, dfunc_len TSRMLS_CC);
-#endif
-
if (zend_hash_add(&dce->function_table, dfunc_lower, dfunc_len + 1, &dfe, sizeof(zend_function), (void*) &dfeInHashTable) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error adding method to class %s::%s()", dclass, dfunc);
efree(dfunc_lower);
return FAILURE;
}
+#ifdef ZEND_ENGINE_2
+ dfeInHashTable->common.scope = dce;
+ dfeInHashTable->common.prototype = _php_runkit_get_method_prototype(dce->parent, dfunc_lower, dfunc_len TSRMLS_CC);
+#endif
+
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, 7,
@@ -632,15 +619,15 @@ PHP_FUNCTION(runkit_method_remove)
RETURN_FALSE;
}
- ancestor_class = php_runkit_locate_scope(ce, fe, methodname, methodname_len);
-
methodname_lower = estrndup(methodname, methodname_len);
if (methodname_lower == NULL) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Not enough memory");
RETURN_FALSE;
}
php_strtolower(methodname_lower, methodname_len);
+ 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, 5, ancestor_class, ce, methodname, methodname_len, fe);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
@@ -0,0 +1,38 @@
+--TEST--
+runkit_method_redefine() must also update children methods' prototypes
+--SKIPIF--
+<?php if(!extension_loaded("runkit") || !RUNKIT_FEATURE_MANIPULATION) print "skip";
+ if(array_shift(explode('.', PHP_VERSION)) < 5) print "skip";
+?>
+--FILE--
+<?php
+class a {
+ protected function foo() {
+ }
+
+ public function test() {
+ $this->foo();
+ }
+}
+
+class b extends a {
+ protected function foo() {
+ }
+}
+
+class c extends b {
+ function bar() {
+ $this->test();
+ }
+}
+
+runkit_method_redefine('a', 'foo', '', 'var_dump("new foo()");');
+
+$c = new c;
+$c->bar();
+
+echo "==DONE==\n";
+
+?>
+--EXPECT--
+==DONE==
Oops, something went wrong.

0 comments on commit a978619

Please sign in to comment.