Skip to content

Commit

Permalink
handle all magic methods (added handling of debugInfo, serialize, uns…
Browse files Browse the repository at this point in the history
…erialize) runkit7#57, runkit7#79
  • Loading branch information
zenovich committed Aug 24, 2015
1 parent 983dbfd commit 30f01e2
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 40 deletions.
8 changes: 7 additions & 1 deletion package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Execute code in restricted environment (sandboxing).

Fixes:
* All ways of adding and removing magic methods and old-style constructors
were reworked and corrected (Thanks to Anthony Dovgal for issue #35).
were reworked and corrected (Thanks to Anthony Dovgal for issues #35, #57, and #79).
* Side effect of redefining, adding and removing class constant (Issue #25) was eliminated
* Adding of non-lowercase default properties was fixed (removed lowercasing)
* Skip the leading slash in class names
Expand Down Expand Up @@ -94,6 +94,7 @@ Execute code in restricted environment (sandboxing).
<file name="_fpm_include.inc" role="test" />
<file name="_fpm_skipif.inc" role="test" />
<file name="runkit_add_magic_methods.phpt" role="test" />
<file name="runkit_add_magic_serialize_method_ignored_for_common_classes.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" />
Expand Down Expand Up @@ -181,6 +182,11 @@ Execute code in restricted environment (sandboxing).
<file name="runkit_methods_redefining_and_cache.phpt" role="test" />
<file name="runkit_methods_returning_by_reference.phpt" role="test" />
<file name="runkit_redefine_old_style_ctor.phpt" role="test" />
<file name="runkit_remove_magic_call_method.phpt" role="test" />
<file name="runkit_remove_magic_callstatic_method.phpt" role="test" />
<file name="runkit_remove_magic_serialize_method.phpt" role="test" />
<file name="runkit_remove_magic_tostring_method.phpt" role="test" />
<file name="runkit_remove_magic_unserialize_method.phpt" role="test" />
<file name="runkit_return_value_used.phpt" role="test" />
<file name="runkit_zval_inspect.phpt" role="test" />
<file name="runkit_static_vars.phpt" role="test" />
Expand Down
59 changes: 46 additions & 13 deletions php_runkit.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#include "Zend/zend_closures.h"
#endif

#include "Zend/zend_interfaces.h"

#define PHP_RUNKIT_VERSION "1.0.4-dev"
#define PHP_RUNKIT_SANDBOX_CLASSNAME "Runkit_Sandbox"
#define PHP_RUNKIT_SANDBOX_PARENT_CLASSNAME "Runkit_Sandbox_Parent"
Expand Down Expand Up @@ -173,6 +175,12 @@ extern ZEND_DECLARE_MODULE_GLOBALS(runkit);
# define zend_hash_quick_del(ht, key, key_len, h) zend_hash_del(ht, key, key_len)
#endif

#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 6) || (PHP_MAJOR_VERSION >= 6)
# define RUNKIT_ABOVE56 1
#else
# define RUNKIT_ABOVE56 0
#endif

#ifdef ZEND_ACC_RETURN_REFERENCE
# define PHP_RUNKIT_ACC_RETURN_REFERENCE ZEND_ACC_RETURN_REFERENCE
#else
Expand Down Expand Up @@ -397,7 +405,7 @@ struct _php_runkit_sandbox_object {
} \
}

inline static void PHP_RUNKIT_ADD_MAGIC_METHOD(zend_class_entry *ce, char *lcmname, int mname_len, zend_function *fe, const zend_function *orig_fe) {
inline static void PHP_RUNKIT_ADD_MAGIC_METHOD(zend_class_entry *ce, char *lcmname, int mname_len, zend_function *fe, const zend_function *orig_fe TSRMLS_DC) {
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))) {
Expand All @@ -424,6 +432,14 @@ inline static void PHP_RUNKIT_ADD_MAGIC_METHOD(zend_class_entry *ce, char *lcmna
} else if (!strncmp((lcmname), ZEND_TOSTRING_FUNC_NAME, (mname_len))) {
(ce)->__tostring = (fe);
#endif
#if RUNKIT_ABOVE56
} else if (!strncmp((lcmname), ZEND_DEBUGINFO_FUNC_NAME, (mname_len))) {
(ce)->__debugInfo = (fe);
#endif
} else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) && !strncmp((lcmname), "serialize", (mname_len))) {
(ce)->serialize_func = (fe);
} else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) && !strncmp((lcmname), "unserialize", (mname_len))) {
(ce)->unserialize_func = (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);
Expand All @@ -437,24 +453,31 @@ inline static void PHP_RUNKIT_ADD_MAGIC_METHOD(zend_class_entry *ce, char *lcmna
}
}

inline static void PHP_RUNKIT_DEL_MAGIC_METHOD(zend_class_entry *ce, const zend_function *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;
inline static void PHP_RUNKIT_DEL_MAGIC_METHOD(zend_class_entry *ce, const zend_function *fe TSRMLS_DC) {
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;
#if RUNKIT_ABOVE53
else if ((ce)->__callstatic == (fe)) (ce)->__callstatic = NULL;
else if ((ce)->__callstatic == (fe)) (ce)->__callstatic = NULL;
#endif
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2 || PHP_MAJOR_VERSION > 5
else if ((ce)->__tostring == (fe)) (ce)->__tostring = NULL;
else if ((ce)->__tostring == (fe)) (ce)->__tostring = NULL;
#endif
#if RUNKIT_ABOVE56
else if ((ce)->__debugInfo == (fe)) (ce)->__debugInfo = NULL;
#endif
else if ((ce)->clone == (fe)) (ce)->clone = NULL;
else if ((ce)->clone == (fe)) (ce)->clone = NULL;
else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) && (ce)->serialize_func == (fe))
(ce)->serialize_func = NULL;
else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) && (ce)->unserialize_func == (fe))
(ce)->unserialize_func = NULL;
}

inline static void PHP_RUNKIT_INHERIT_MAGIC(zend_class_entry *ce, const zend_function *fe, const zend_function *orig_fe) {
inline static void PHP_RUNKIT_INHERIT_MAGIC(zend_class_entry *ce, const zend_function *fe, const zend_function *orig_fe TSRMLS_DC) {
if ((ce)->__get == (orig_fe) && (ce)->parent->__get == (fe)) {
(ce)->__get = (ce)->parent->__get;
} else if ((ce)->__set == (orig_fe) && (ce)->parent->__set == (fe)) {
Expand All @@ -479,6 +502,16 @@ inline static void PHP_RUNKIT_INHERIT_MAGIC(zend_class_entry *ce, const zend_fun
(ce)->destructor = (ce)->parent->destructor;
} else if ((ce)->constructor == (orig_fe) && (ce)->parent->constructor == (fe)) {
(ce)->constructor = (ce)->parent->constructor;
#if RUNKIT_ABOVE56
} else if ((ce)->__debugInfo == (orig_fe) && (ce)->parent->__debugInfo == (fe)) {
(ce)->__debugInfo = (ce)->parent->__debugInfo;
#endif
} else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) &&
(ce)->serialize_func == (orig_fe) && (ce)->parent->serialize_func == (fe)) {
(ce)->serialize_func = (ce)->parent->serialize_func;
} else if (instanceof_function_ex(ce, zend_ce_serializable, 1 TSRMLS_CC) &&
(ce)->unserialize_func == (orig_fe) && (ce)->parent->unserialize_func == (fe)) {
(ce)->unserialize_func = (ce)->parent->unserialize_func;
}
}

Expand Down
4 changes: 2 additions & 2 deletions runkit_classes.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ static int php_runkit_remove_inherited_methods(zend_function *fe, zend_class_ent

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_lower, fname_lower_len, fe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe TSRMLS_CC);
php_runkit_remove_function_from_reflection_objects(fe TSRMLS_CC);

efree(fname_lower);
Expand Down Expand Up @@ -166,7 +166,7 @@ static int php_runkit_inherit_methods(zend_function *fe, zend_class_entry *ce TS
}

PHP_RUNKIT_FUNCTION_ADD_REF(fe);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fname_lower, fname_lower_len, fe, NULL);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, fname_lower, fname_lower_len, fe, NULL 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_lower, fname_lower_len, NULL);
Expand Down
2 changes: 1 addition & 1 deletion runkit_import.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ static int php_runkit_import_class_methods(zend_class_entry *dce, zend_class_ent
zend_hash_move_forward_ex(&ce->function_table, &pos);
continue;
}
PHP_RUNKIT_ADD_MAGIC_METHOD(dce, fn, fn_len, fe, dfe);
PHP_RUNKIT_ADD_MAGIC_METHOD(dce, fn, fn_len, fe, dfe TSRMLS_CC);
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, 0);
zend_hash_move_forward_ex(&ce->function_table, &pos);
Expand Down
14 changes: 7 additions & 7 deletions runkit_methods.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ 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);
PHP_RUNKIT_INHERIT_MAGIC(ce, fe, orig_fe TSRMLS_CC);

/* 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, 6,
Expand Down Expand Up @@ -275,7 +275,7 @@ int php_runkit_clean_children_methods(RUNKIT_53_TSRMLS_ARG(zend_class_entry *ce)

zend_hash_del(&ce->function_table, fname_lower, fname_lower_len + 1);

PHP_RUNKIT_DEL_MAGIC_METHOD(ce, orig_cfe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, orig_cfe TSRMLS_CC);

return ZEND_HASH_APPLY_KEEP;
}
Expand Down Expand Up @@ -392,7 +392,7 @@ static void php_runkit_method_add_or_update(INTERNAL_FUNCTION_PARAMETERS, int ad
fe->common.scope = ce;
fe->common.prototype = _php_runkit_get_method_prototype(ce->parent, methodname_lower, methodname_lower_len TSRMLS_CC);

PHP_RUNKIT_ADD_MAGIC_METHOD(ce, methodname_lower, methodname_lower_len, fe, orig_fe);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, methodname_lower, methodname_lower_len, fe, orig_fe 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, methodname_lower, methodname_lower_len, orig_fe);

Expand Down Expand Up @@ -443,7 +443,7 @@ static int php_runkit_method_copy(const char *dclass, int dclass_len, const char
dfeInHashTable->common.scope = dce;
dfeInHashTable->common.prototype = _php_runkit_get_method_prototype(dce->parent, dfunc_lower, dfunc_lower_len TSRMLS_CC);

PHP_RUNKIT_ADD_MAGIC_METHOD(dce, dfunc_lower, dfunc_lower_len, dfeInHashTable, NULL);
PHP_RUNKIT_ADD_MAGIC_METHOD(dce, dfunc_lower, dfunc_lower_len, dfeInHashTable, NULL TSRMLS_CC);

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_lower_len, NULL, 0);
Expand Down Expand Up @@ -522,7 +522,7 @@ PHP_FUNCTION(runkit_method_remove)
}

efree(methodname_lower);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe TSRMLS_CC);

RETURN_TRUE;
}
Expand Down Expand Up @@ -603,7 +603,7 @@ PHP_FUNCTION(runkit_method_rename)
RETURN_FALSE;
}

PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe);
PHP_RUNKIT_DEL_MAGIC_METHOD(ce, fe TSRMLS_CC);

if (php_runkit_fetch_class_method(classname, classname_len, newname, newname_len, &ce, &fe TSRMLS_CC) == FAILURE) {
efree(newname_lower);
Expand All @@ -615,7 +615,7 @@ PHP_FUNCTION(runkit_method_rename)
fe->common.scope = ce;
fe->common.prototype = _php_runkit_get_method_prototype(ce->parent, newname_lower, newname_lower_len TSRMLS_CC);;

PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname_lower, newname_lower_len, fe, NULL);
PHP_RUNKIT_ADD_MAGIC_METHOD(ce, newname_lower, newname_lower_len, fe, NULL TSRMLS_CC);
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_lower, newname_lower_len, NULL, 0);

Expand Down
93 changes: 77 additions & 16 deletions tests/runkit_add_magic_methods.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ adding and removing magic methods
?>
--FILE--
<?php

class Test {
class Test implements Serializable {
function serialize() {}
function unserialize($s) {}
}

class FOO_test extends test {
Expand All @@ -24,6 +25,11 @@ 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);
runkit_method_add("Test", "__clone", "", 'echo "__clone\n";');
runkit_method_add("Test", "__tostring", "", 'return "__tostring\n";');
runkit_method_add("Test", "__debuginfo", "", 'echo "__debuginfo\n"; ob_start(); return array();');
runkit_method_redefine("Test", "serialize", "", 'echo "serialize\n";return "";');
runkit_method_redefine("Test", "unserialize", "", 'echo "unserialize\n";');
$a = new test;
$b = new foo_test;
$c = new FOO_test_Child;
Expand All @@ -42,9 +48,15 @@ unset($c->test);
isset($a->test);
isset($b->test);
isset($c->test);
$a = NULL;
$b = NULL;
$c = NULL;
$s1 = serialize($a);
$s2 = serialize($b);
$s3 = serialize($c);
$ua = unserialize($s1);
$ub = unserialize($s2);
$uc = unserialize($s3);
$ca = clone $a;
$cb = clone $b;
$cc = clone $c;
if(version_compare(PHP_VERSION, '5.2.999', '>')) {
Test::method();
FOO_Test::method();
Expand All @@ -55,14 +67,42 @@ if(version_compare(PHP_VERSION, '5.2.999', '>')) {
echo "__callstatic\n";
}

if(version_compare(PHP_VERSION, '5.1.999', '>')) {
echo $a;
echo $b;
echo $c;
} else {
echo "__tostring\n";
echo "__tostring\n";
echo "__tostring\n";
}

if(version_compare(PHP_VERSION, '5.5.999', '>')) {
var_dump($a);
ob_end_clean();
var_dump($b);
ob_end_clean();
var_dump($c);
ob_end_clean();
} else {
echo "__debuginfo\n";
echo "__debuginfo\n";
echo "__debuginfo\n";
}
$a = NULL;
$b = NULL;
$c = NULL;


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");
runkit_method_remove("Test", "__clone");
runkit_method_remove("Test", "__tostring");
runkit_method_remove("Test", "__debuginfo");
echo "after removing\n";

$a = new test;
Expand All @@ -74,19 +114,21 @@ $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);
$ca = clone $a;
$cb = clone $b;
$cc = clone $c;
var_dump($a);
var_dump($b);
var_dump($c);
$a = NULL;
$b = NULL;
$c = NULL;
FOO_Test_child::method();
?>
--EXPECTF--
__construct
Expand All @@ -107,18 +149,37 @@ __unset
__isset
__isset
__isset
__destruct
__destruct
__destruct
serialize
serialize
serialize
unserialize
unserialize
unserialize
__clone
__clone
__clone
__callstatic
__callstatic
__callstatic
__tostring
__tostring
__tostring
__debuginfo
__debuginfo
__debuginfo
__destruct
__destruct
__destruct
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
object(Test)#3 (0) {
}
object(FOO_test)#2 (0) {
}
object(FOO_test_Child)#1 (0) {
}
Loading

0 comments on commit 30f01e2

Please sign in to comment.