Skip to content

fix bug #63462 (Magic methods called twice for unset protected properties) #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ PHP NEWS
. Fixed bug #63943 (Bad warning text from strpos() on empty needle).
(Laruence)
. Fixed bug #63882 (zend_std_compare_objects crash on recursion). (Dmitry)
. Fixed bug #63462 (Magic methods called twice for unset protected
properties). (Stas)

- Litespeed:
. Fixed bug #63228 (-Werror=format-security error in lsapi code). (George)
Expand Down
74 changes: 74 additions & 0 deletions Zend/tests/bug63462.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
--TEST--
Test script to verify that magic methods should be called only once when accessing an unset property.
--CREDITS--
Marco Pivetta <ocramius@gmail.com>
--FILE--
<?php
class Test {
public $publicProperty;
protected $protectedProperty;
private $privateProperty;

public function __construct() {
unset(
$this->publicProperty,
$this->protectedProperty,
$this->privateProperty
);
}

function __get($name) {
echo '__get ' . $name . "\n";
return $this->$name;
}

function __set($name, $value) {
echo '__set ' . $name . "\n";
$this->$name = $value;
}

function __isset($name) {
echo '__isset ' . $name . "\n";
return isset($this->$name);
}
}

$test = new Test();

$test->nonExisting;
$test->publicProperty;
$test->protectedProperty;
$test->privateProperty;
isset($test->nonExisting);
isset($test->publicProperty);
isset($test->protectedProperty);
isset($test->privateProperty);
$test->nonExisting = 'value';
$test->publicProperty = 'value';
$test->protectedProperty = 'value';
$test->privateProperty = 'value';

?>

--EXPECTF--
__get nonExisting

Notice: Undefined property: Test::$nonExisting in %s on line %d
__get publicProperty

Notice: Undefined property: Test::$publicProperty in %s on line %d
__get protectedProperty

Notice: Undefined property: Test::$protectedProperty in %s on line %d
__get privateProperty

Notice: Undefined property: Test::$privateProperty in %s on line %d
__isset nonExisting
__isset publicProperty
__isset protectedProperty
__isset privateProperty
__set nonExisting
__set publicProperty
__set protectedProperty
__set privateProperty

10 changes: 10 additions & 0 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,16 @@ static int zend_get_property_guard(zend_object *zobj, zend_property_info *proper
info.name = Z_STRVAL_P(member);
info.name_length = Z_STRLEN_P(member);
info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
} else if(property_info->name[0] == '\0'){
const char *class_name = NULL, *prop_name = NULL;
zend_unmangle_property_name(property_info->name, property_info->name_length, &class_name, &prop_name);
if(class_name) {
/* use unmangled name for protected properties */
info.name = prop_name;
info.name_length = strlen(prop_name);
info.h = zend_get_hash_value(info.name, info.name_length+1);
property_info = &info;
}
}
if (!zobj->guards) {
ALLOC_HASHTABLE(zobj->guards);
Expand Down