Skip to content

Commit

Permalink
fix bug with inherited old-style constructors
Browse files Browse the repository at this point in the history
we need some additional safechecks when redefining/adding these cursed
old-style ctors
  • Loading branch information
tony2001 committed Sep 12, 2012
1 parent 8c73eaf commit d63c984
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 21 deletions.
9 changes: 5 additions & 4 deletions php_runkit.h
Expand Up @@ -335,9 +335,10 @@ 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; } \
#define PHP_RUNKIT_ADD_MAGIC_METHOD(ce, method, fe, orig_fe) { \
if ((strcasecmp((method), (ce)->name) == 0) || \
(strcmp((method), "__construct") == 0)) { (ce)->constructor = (fe); (fe)->common.fn_flags = ZEND_ACC_CTOR; } \
else if ((ce)->constructor && (ce)->constructor == orig_fe) { (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); \
Expand All @@ -355,7 +356,7 @@ struct _php_runkit_sandbox_object {
#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_ADD_MAGIC_METHOD(ce, method, fe, orig_fe)
#define PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe)
#endif /* ZEND_ENGINE_2 */
#endif /* PHP_RUNKIT_MANIPULATION */
Expand Down
2 changes: 1 addition & 1 deletion runkit_classes.c
Expand Up @@ -95,7 +95,7 @@ static int php_runkit_inherit_methods(zend_function *fe, zend_class_entry *ce TS
}
efree(lower_function_name);

PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fe->common.function_name, fe);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fe->common.function_name, fe, NULL);

return ZEND_HASH_APPLY_KEEP;
}
Expand Down
54 changes: 38 additions & 16 deletions runkit_methods.c
Expand Up @@ -270,6 +270,8 @@ 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*);
int add_or_update = va_arg(args, int);
zend_function *cfe = NULL;
char *fname_lower;
RUNKIT_UNDER53_TSRMLS_FETCH();
Expand Down Expand Up @@ -312,10 +314,29 @@ int php_runkit_update_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce
}
PHP_RUNKIT_FUNCTION_ADD_REF(fe);

PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fname, fe);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fname, fe, orig_fe);

/* 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, add_or_update);

if (add_or_update == HASH_ADD && ce->parent->constructor == fe) {
/* special case for old-style Foo::Foo() constructors */
zend_function *ctor;
char *lc_class_name;

lc_class_name = estrndup(ce->name, ce->name_length);
php_strtolower(lc_class_name, ce->name_length);

if (zend_hash_find(&ce->function_table, "__constructor", sizeof("__constructor"), (void **)&ctor)==SUCCESS) {
/* if the class has it's own ctor, we don't have to do anything at all */
} else if (zend_hash_find(&ce->function_table, lc_class_name, ce->name_length + 1, (void **)&ctor)==SUCCESS) {
/* --//-- */
} else {
/* if not, then we have inherit parent class ctor */
ce->constructor = ce->parent->constructor;
}
efree(lc_class_name);
}

efree(fname_lower);

Expand All @@ -332,6 +353,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();
Expand Down Expand Up @@ -368,11 +390,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;
Expand Down Expand Up @@ -447,7 +469,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
Expand Down Expand Up @@ -489,6 +511,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.");
Expand Down Expand Up @@ -534,11 +557,7 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
}
#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 (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;
Expand All @@ -557,7 +576,10 @@ 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, fe, orig_fe);
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, add_or_update);

efree(methodname_lower);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
Expand Down Expand Up @@ -611,9 +633,9 @@ 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, 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, &dfe, dfunc_lower, dfunc_len, NULL, HASH_ADD);

efree(dfunc_lower);
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
Expand Down Expand Up @@ -678,7 +700,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 (zend_hash_del(&ce->function_table, methodname_lower, methodname_len + 1) == FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to remove method from class");
Expand Down Expand Up @@ -777,9 +799,9 @@ PHP_FUNCTION(runkit_method_rename)

efree(newname_lower);
efree(methodname_lower);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname, fe);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname, fe, NULL);

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);
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, HASH_UPDATE);

#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5)
php_runkit_clear_all_functions_runtime_cache(TSRMLS_C);
Expand Down
19 changes: 19 additions & 0 deletions tests/runkit_add_old_style_ctor.phpt
@@ -0,0 +1,19 @@
--TEST--
add old-style parent ctor
--FILE--
<?php

class Test {
}

class FOO_test extends test {
}

runkit_method_add("test", "test", "", "var_dump('new constructor');");
$a = new foo_test;

echo "==DONE==\n";
?>
--EXPECT--
string(15) "new constructor"
==DONE==
22 changes: 22 additions & 0 deletions tests/runkit_add_old_style_ctor1.phpt
@@ -0,0 +1,22 @@
--TEST--
add old-style parent ctor (existing ctor)
--FILE--
<?php

class Test {
}

class FOO_test extends test {
function foo_test() {
var_dump("foo_test ctor");
}
}

runkit_method_add("test", "test", "", "var_dump('new constructor');");
$a = new foo_test;

echo "==DONE==\n";
?>
--EXPECT--
string(13) "foo_test ctor"
==DONE==
22 changes: 22 additions & 0 deletions tests/runkit_redefine_old_style_ctor.phpt
@@ -0,0 +1,22 @@
--TEST--
redefine old-style parent ctor
--FILE--
<?php

class Test {
function test() {
var_dump("original constructor");
}
}

class FOO_test extends test {
}

runkit_method_redefine("test", "test", "", "var_dump('new constructor');");
$a = new foo_test;

echo "==DONE==\n";
?>
--EXPECT--
string(15) "new constructor"
==DONE==

0 comments on commit d63c984

Please sign in to comment.