Skip to content

Commit 64931b6

Browse files
committed
Allow recursive calls to __get/__set for different properties
1 parent a8c6b99 commit 64931b6

File tree

6 files changed

+84
-53
lines changed

6 files changed

+84
-53
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? Nov 2005, PHP 5.1
4+
- Allow recursive calls to __get/__set for different properties. (Dmitry)
45
- Upgraded PEAR to version 1.4.4. (Greg)
56
- Fixed bug in mysqli extension with unsigned int(11) being represented as
67
signed integer in PHP instead of string in 32bit systems. (Andrey)

Zend/zend.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,18 @@ void zend_error_noreturn(int type, const char *format, ...) __attribute__ ((nore
265265
typedef struct _zval_struct zval;
266266
typedef struct _zend_class_entry zend_class_entry;
267267

268+
typedef struct _zend_guard {
269+
zend_bool in_get;
270+
zend_bool in_set;
271+
zend_bool in_unset;
272+
zend_bool in_isset;
273+
zend_bool dummy; /* sizeof(zend_guard) must not be equal to sizeof(void*) */
274+
} zend_guard;
275+
268276
typedef struct _zend_object {
269277
zend_class_entry *ce;
270278
HashTable *properties;
271-
unsigned int in_get:1;
272-
unsigned int in_set:1;
273-
unsigned int in_unset:1;
274-
unsigned int in_isset:1;
279+
HashTable *guards; /* protects from __get/__set ... recursion */
275280
} zend_object;
276281

277282
typedef unsigned int zend_object_handle;

Zend/zend_object_handlers.c

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ ZEND_API struct _zend_property_info *zend_get_property_info(zend_class_entry *ce
237237
EG(std_property_info).flags = ZEND_ACC_PUBLIC;
238238
EG(std_property_info).name = Z_STRVAL_P(member);
239239
EG(std_property_info).name_length = Z_STRLEN_P(member);
240-
EG(std_property_info).h = zend_get_hash_value(EG(std_property_info).name, EG(std_property_info).name_length+1);
240+
EG(std_property_info).h = h;
241241
property_info = &EG(std_property_info);
242242
}
243243
return property_info;
@@ -268,6 +268,30 @@ ZEND_API int zend_check_property_access(zend_object *zobj, char *prop_info_name
268268
return zend_verify_property_access(property_info, zobj->ce TSRMLS_CC) ? SUCCESS : FAILURE;
269269
}
270270

271+
static int zend_get_property_guard(zend_object *zobj, zend_property_info *property_info, zval *member, zend_guard **pguard)
272+
{
273+
zend_property_info info;
274+
zend_guard stub;
275+
276+
if (!property_info) {
277+
property_info = &info;
278+
info.name = Z_STRVAL_P(member);
279+
info.name_length = Z_STRLEN_P(member);
280+
info.h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
281+
}
282+
if (!zobj->guards) {
283+
ALLOC_HASHTABLE(zobj->guards);
284+
zend_hash_init(zobj->guards, 0, NULL, NULL, 0);
285+
} else if (zend_hash_quick_find(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void **) pguard) == SUCCESS) {
286+
return SUCCESS;
287+
}
288+
stub.in_get = 0;
289+
stub.in_set = 0;
290+
stub.in_unset = 0;
291+
stub.in_isset = 0;
292+
return zend_hash_quick_add(zobj->guards, property_info->name, property_info->name_length+1, property_info->h, (void**)&stub, sizeof(stub), (void**) pguard);
293+
}
294+
271295
zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
272296
{
273297
zend_object *zobj;
@@ -276,11 +300,9 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
276300
zval *rv = NULL;
277301
zend_property_info *property_info;
278302
int silent;
279-
zend_bool use_get;
280303

281304
silent = (type == BP_VAR_IS);
282305
zobj = Z_OBJ_P(object);
283-
use_get = (zobj->ce->__get && !zobj->in_get);
284306

285307
if (member->type != IS_STRING) {
286308
ALLOC_ZVAL(tmp_member);
@@ -296,14 +318,18 @@ zval *zend_std_read_property(zval *object, zval *member, int type TSRMLS_DC)
296318
#endif
297319

298320
/* make zend_get_property_info silent if we have getter - we may want to use it */
299-
property_info = zend_get_property_info(zobj->ce, member, use_get TSRMLS_CC);
321+
property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
300322

301323
if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
302-
if (use_get) {
324+
zend_guard *guard;
325+
326+
if (zobj->ce->__get &&
327+
zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
328+
!guard->in_get) {
303329
/* have getter - try with it! */
304-
zobj->in_get = 1; /* prevent circular getting */
330+
guard->in_get = 1; /* prevent circular getting */
305331
rv = zend_std_call_getter(object, member TSRMLS_CC);
306-
zobj->in_get = 0;
332+
guard->in_get = 0;
307333

308334
if (rv) {
309335
retval = &rv;
@@ -333,10 +359,8 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
333359
zval **variable_ptr;
334360
int setter_done = 0;
335361
zend_property_info *property_info;
336-
zend_bool use_set;
337362

338363
zobj = Z_OBJ_P(object);
339-
use_set = (zobj->ce->__set && !zobj->in_set);
340364

341365
if (member->type != IS_STRING) {
342366
ALLOC_ZVAL(tmp_member);
@@ -347,7 +371,7 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
347371
member = tmp_member;
348372
}
349373

350-
property_info = zend_get_property_info(zobj->ce, member, use_set TSRMLS_CC);
374+
property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__set != NULL) TSRMLS_CC);
351375

352376
if (property_info && zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS) {
353377
if (*variable_ptr == value) {
@@ -368,13 +392,17 @@ static void zend_std_write_property(zval *object, zval *member, zval *value TSRM
368392
}
369393
}
370394
} else {
371-
if (use_set) {
372-
zobj->in_set = 1; /* prevent circular setting */
395+
zend_guard *guard;
396+
397+
if (zobj->ce->__set &&
398+
zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
399+
!guard->in_set) {
400+
guard->in_set = 1; /* prevent circular setting */
373401
if (zend_std_call_setter(object, member, value TSRMLS_CC) != SUCCESS) {
374402
/* for now, just ignore it - __set should take care of warnings, etc. */
375403
}
376404
setter_done = 1;
377-
zobj->in_set = 0;
405+
guard->in_set = 0;
378406
}
379407
}
380408

@@ -482,10 +510,8 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
482510
zval tmp_member;
483511
zval **retval;
484512
zend_property_info *property_info;
485-
zend_bool use_get;
486513

487514
zobj = Z_OBJ_P(object);
488-
use_get = (zobj->ce->__get && !zobj->in_get);
489515

490516
if (member->type != IS_STRING) {
491517
tmp_member = *member;
@@ -498,12 +524,15 @@ static zval **zend_std_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC
498524
fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
499525
#endif
500526

501-
property_info = zend_get_property_info(zobj->ce, member, use_get TSRMLS_CC);
527+
property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__get != NULL) TSRMLS_CC);
502528

503529
if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &retval) == FAILURE) {
504530
zval *new_zval;
531+
zend_guard *guard;
505532

506-
if (!use_get) {
533+
if (!zobj->ce->__get ||
534+
zend_get_property_guard(zobj, property_info, member, &guard) != SUCCESS ||
535+
guard->in_get) {
507536
/* we don't have access controls - will just add it */
508537
new_zval = &EG(uninitialized_zval);
509538

@@ -527,10 +556,8 @@ static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC)
527556
zend_object *zobj;
528557
zval *tmp_member = NULL;
529558
zend_property_info *property_info;
530-
zend_bool use_unset;
531559

532560
zobj = Z_OBJ_P(object);
533-
use_unset = (zobj->ce->__unset && !zobj->in_unset);
534561

535562
if (member->type != IS_STRING) {
536563
ALLOC_ZVAL(tmp_member);
@@ -541,14 +568,18 @@ static void zend_std_unset_property(zval *object, zval *member TSRMLS_DC)
541568
member = tmp_member;
542569
}
543570

544-
property_info = zend_get_property_info(zobj->ce, member, 0 TSRMLS_CC);
571+
property_info = zend_get_property_info(zobj->ce, member, (zobj->ce->__unset != NULL) TSRMLS_CC);
545572

546573
if (!property_info || zend_hash_del(zobj->properties, property_info->name, property_info->name_length+1) == FAILURE) {
547-
if (use_unset) {
574+
zend_guard *guard;
575+
576+
if (zobj->ce->__unset &&
577+
zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
578+
!guard->in_unset) {
548579
/* have unseter - try with it! */
549-
zobj->in_unset = 1; /* prevent circular unsetting */
580+
guard->in_unset = 1; /* prevent circular setting */
550581
zend_std_call_unsetter(object, member TSRMLS_CC);
551-
zobj->in_unset = 0;
582+
guard->in_unset = 0;
552583
}
553584
}
554585

@@ -895,10 +926,8 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
895926
zval **value;
896927
zval *tmp_member = NULL;
897928
zend_property_info *property_info;
898-
zend_bool use_isset;
899929

900930
zobj = Z_OBJ_P(object);
901-
use_isset = (zobj->ce->__isset && !zobj->in_isset);
902931

903932
if (member->type != IS_STRING) {
904933
ALLOC_ZVAL(tmp_member);
@@ -916,26 +945,33 @@ static int zend_std_has_property(zval *object, zval *member, int has_set_exists
916945
property_info = zend_get_property_info(zobj->ce, member, 1 TSRMLS_CC);
917946

918947
if (!property_info || zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &value) == FAILURE) {
948+
zend_guard *guard;
949+
919950
result = 0;
920-
if (use_isset && (has_set_exists != 2)) {
951+
if ((has_set_exists != 2) &&
952+
zobj->ce->__isset &&
953+
zend_get_property_guard(zobj, property_info, member, &guard) == SUCCESS &&
954+
!guard->in_isset) {
921955
zval *rv;
922956

923957
/* have issetter - try with it! */
924-
zobj->in_isset = 1; /* prevent circular getting */
958+
guard->in_isset = 1; /* prevent circular getting */
925959
rv = zend_std_call_issetter(object, member TSRMLS_CC);
926-
zobj->in_isset = 0;
927960
if (rv) {
928961
result = zend_is_true(rv);
929962
zval_ptr_dtor(&rv);
930-
if (has_set_exists && result && !EG(exception) && zobj->ce->__get && !zobj->in_get) {
963+
if (has_set_exists && result && !EG(exception) && zobj->ce->__get && !guard->in_get) {
964+
guard->in_get = 1;
931965
rv = zend_std_call_getter(object, member TSRMLS_CC);
966+
guard->in_get = 0;
932967
if (rv) {
933968
rv->refcount++;
934969
result = i_zend_is_true(rv);
935970
zval_ptr_dtor(&rv);
936971
}
937972
}
938973
}
974+
guard->in_isset = 0;
939975
}
940976
} else {
941977
switch (has_set_exists) {

Zend/zend_objects.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object, zend_object_handl
8888

8989
ZEND_API void zend_objects_free_object_storage(zend_object *object TSRMLS_DC)
9090
{
91+
if (object->guards) {
92+
zend_hash_destroy(object->guards);
93+
FREE_HASHTABLE(object->guards);
94+
}
9195
zend_hash_destroy(object->properties);
9296
FREE_HASHTABLE(object->properties);
9397
efree(object);
@@ -101,10 +105,7 @@ ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_ent
101105
(*object)->ce = class_type;
102106
retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_objects_free_object_storage, NULL TSRMLS_CC);
103107
retval.handlers = &std_object_handlers;
104-
(*object)->in_get = 0;
105-
(*object)->in_set = 0;
106-
(*object)->in_unset = 0;
107-
(*object)->in_isset = 0;
108+
(*object)->guards = NULL;
108109
return retval;
109110
}
110111

Zend/zend_reflection_api.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,7 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
203203

204204
*intern_clone = emalloc(sizeof(reflection_object));
205205
(*intern_clone)->zo.ce = intern->zo.ce;
206-
(*intern_clone)->zo.in_get = 0;
207-
(*intern_clone)->zo.in_set = 0;
208-
(*intern_clone)->zo.in_unset = 0;
209-
(*intern_clone)->zo.in_isset = 0;
206+
(*intern_clone)->zo.guards = NULL;
210207
ALLOC_HASHTABLE((*intern_clone)->zo.properties);
211208
(*intern_clone)->ptr = intern->ptr;
212209
(*intern_clone)->free_ptr = intern->free_ptr;
@@ -224,10 +221,7 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
224221

225222
intern = emalloc(sizeof(reflection_object));
226223
intern->zo.ce = class_type;
227-
intern->zo.in_get = 0;
228-
intern->zo.in_set = 0;
229-
intern->zo.in_unset = 0;
230-
intern->zo.in_isset = 0;
224+
intern->zo.guards = NULL;
231225
intern->ptr = NULL;
232226
intern->obj = NULL;
233227
intern->free_ptr = 0;

ext/reflection/php_reflection.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,7 @@ static void reflection_objects_clone(void *object, void **object_clone TSRMLS_DC
203203

204204
*intern_clone = emalloc(sizeof(reflection_object));
205205
(*intern_clone)->zo.ce = intern->zo.ce;
206-
(*intern_clone)->zo.in_get = 0;
207-
(*intern_clone)->zo.in_set = 0;
208-
(*intern_clone)->zo.in_unset = 0;
209-
(*intern_clone)->zo.in_isset = 0;
206+
(*intern_clone)->zo.guards = NULL;
210207
ALLOC_HASHTABLE((*intern_clone)->zo.properties);
211208
(*intern_clone)->ptr = intern->ptr;
212209
(*intern_clone)->free_ptr = intern->free_ptr;
@@ -224,10 +221,7 @@ static zend_object_value reflection_objects_new(zend_class_entry *class_type TSR
224221

225222
intern = emalloc(sizeof(reflection_object));
226223
intern->zo.ce = class_type;
227-
intern->zo.in_get = 0;
228-
intern->zo.in_set = 0;
229-
intern->zo.in_unset = 0;
230-
intern->zo.in_isset = 0;
224+
intern->zo.guards = NULL;
231225
intern->ptr = NULL;
232226
intern->obj = NULL;
233227
intern->free_ptr = 0;

0 commit comments

Comments
 (0)