1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2017 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Dmitry Stogov <dmitry@zend.com> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id$ */
22
23 #include "zend.h"
24 #include "zend_globals.h"
25 #include "zend_variables.h"
26 #include "zend_API.h"
27 #include "zend_objects.h"
28 #include "zend_objects_API.h"
29 #include "zend_object_handlers.h"
30 #include "zend_interfaces.h"
31 #include "zend_exceptions.h"
32 #include "zend_closures.h"
33 #include "zend_compile.h"
34 #include "zend_hash.h"
35
36 #define DEBUG_OBJECT_HANDLERS 0
37
38 /* guard flags */
39 #define IN_GET (1<<0)
40 #define IN_SET (1<<1)
41 #define IN_UNSET (1<<2)
42 #define IN_ISSET (1<<3)
43
44 #define Z_OBJ_PROTECT_RECURSION(zval_p) \
45 do { \
46 if (Z_OBJ_APPLY_COUNT_P(zval_p) >= 3) { \
47 zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?"); \
48 } \
49 Z_OBJ_INC_APPLY_COUNT_P(zval_p); \
50 } while (0)
51
52
53 #define Z_OBJ_UNPROTECT_RECURSION(zval_p) \
54 Z_OBJ_DEC_APPLY_COUNT_P(zval_p)
55
56 /*
57 __X accessors explanation:
58
59 if we have __get and property that is not part of the properties array is
60 requested, we call __get handler. If it fails, we return uninitialized.
61
62 if we have __set and property that is not part of the properties array is
63 set, we call __set handler. If it fails, we do not change the array.
64
65 for both handlers above, when we are inside __get/__set, no further calls for
66 __get/__set for this property of this object will be made, to prevent endless
67 recursion and enable accessors to change properties array.
68
69 if we have __call and method which is not part of the class function table is
70 called, we cal __call handler.
71 */
72
rebuild_object_properties(zend_object * zobj)73 ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
74 {
75 if (!zobj->properties) {
76 zend_property_info *prop_info;
77 zend_class_entry *ce = zobj->ce;
78
79 ALLOC_HASHTABLE(zobj->properties);
80 zend_hash_init(zobj->properties, ce->default_properties_count, NULL, ZVAL_PTR_DTOR, 0);
81 if (ce->default_properties_count) {
82 zend_hash_real_init(zobj->properties, 0);
83 zobj->properties->nInternalPointer = 0;
84 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
85 if (/*prop_info->ce == ce &&*/
86 (prop_info->flags & ZEND_ACC_STATIC) == 0) {
87
88 if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
89 zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
90 }
91
92 _zend_hash_append_ind(zobj->properties, prop_info->name,
93 OBJ_PROP(zobj, prop_info->offset));
94 }
95 } ZEND_HASH_FOREACH_END();
96 while (ce->parent && ce->parent->default_properties_count) {
97 ce = ce->parent;
98 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) {
99 if (prop_info->ce == ce &&
100 (prop_info->flags & ZEND_ACC_STATIC) == 0 &&
101 (prop_info->flags & ZEND_ACC_PRIVATE) != 0) {
102 zval zv;
103
104 if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
105 zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
106 }
107
108 ZVAL_INDIRECT(&zv, OBJ_PROP(zobj, prop_info->offset));
109 zend_hash_add(zobj->properties, prop_info->name, &zv);
110 }
111 } ZEND_HASH_FOREACH_END();
112 }
113 }
114 }
115 }
116 /* }}} */
117
zend_std_get_properties(zval * object)118 ZEND_API HashTable *zend_std_get_properties(zval *object) /* {{{ */
119 {
120 zend_object *zobj;
121 zobj = Z_OBJ_P(object);
122 if (!zobj->properties) {
123 rebuild_object_properties(zobj);
124 }
125 return zobj->properties;
126 }
127 /* }}} */
128
zend_std_get_gc(zval * object,zval ** table,int * n)129 ZEND_API HashTable *zend_std_get_gc(zval *object, zval **table, int *n) /* {{{ */
130 {
131 if (Z_OBJ_HANDLER_P(object, get_properties) != zend_std_get_properties) {
132 *table = NULL;
133 *n = 0;
134 return Z_OBJ_HANDLER_P(object, get_properties)(object);
135 } else {
136 zend_object *zobj = Z_OBJ_P(object);
137
138 if (zobj->properties) {
139 *table = NULL;
140 *n = 0;
141 return zobj->properties;
142 } else {
143 *table = zobj->properties_table;
144 *n = zobj->ce->default_properties_count;
145 return NULL;
146 }
147 }
148 }
149 /* }}} */
150
zend_std_get_debug_info(zval * object,int * is_temp)151 ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp) /* {{{ */
152 {
153 zend_class_entry *ce = Z_OBJCE_P(object);
154 zval retval;
155 HashTable *ht;
156
157 if (!ce->__debugInfo) {
158 *is_temp = 0;
159 return Z_OBJ_HANDLER_P(object, get_properties)
160 ? Z_OBJ_HANDLER_P(object, get_properties)(object)
161 : NULL;
162 }
163
164 zend_call_method_with_0_params(object, ce, &ce->__debugInfo, ZEND_DEBUGINFO_FUNC_NAME, &retval);
165 if (Z_TYPE(retval) == IS_ARRAY) {
166 if (Z_IMMUTABLE(retval)) {
167 *is_temp = 1;
168 return zend_array_dup(Z_ARRVAL(retval));
169 } else if (Z_REFCOUNT(retval) <= 1) {
170 *is_temp = 1;
171 ht = Z_ARR(retval);
172 return ht;
173 } else {
174 *is_temp = 0;
175 zval_ptr_dtor(&retval);
176 return Z_ARRVAL(retval);
177 }
178 } else if (Z_TYPE(retval) == IS_NULL) {
179 *is_temp = 1;
180 ALLOC_HASHTABLE(ht);
181 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
182 return ht;
183 }
184
185 zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");
186
187 return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
188 }
189 /* }}} */
190
zend_std_call_getter(zval * object,zval * member,zval * retval)191 static void zend_std_call_getter(zval *object, zval *member, zval *retval) /* {{{ */
192 {
193 zend_class_entry *ce = Z_OBJCE_P(object);
194
195 /* __get handler is called with one argument:
196 property name
197
198 it should return whether the call was successful or not
199 */
200 zend_call_method_with_1_params(object, ce, &ce->__get, ZEND_GET_FUNC_NAME, retval, member);
201 }
202 /* }}} */
203
zend_std_call_setter(zval * object,zval * member,zval * value)204 static int zend_std_call_setter(zval *object, zval *member, zval *value) /* {{{ */
205 {
206 zval retval;
207 int result;
208 zend_class_entry *ce = Z_OBJCE_P(object);
209
210 /* __set handler is called with two arguments:
211 property name
212 value to be set
213
214 it should return whether the call was successful or not
215 */
216 zend_call_method_with_2_params(object, ce, &ce->__set, ZEND_SET_FUNC_NAME, &retval, member, value);
217
218 if (Z_TYPE(retval) != IS_UNDEF) {
219 result = i_zend_is_true(&retval) ? SUCCESS : FAILURE;
220 zval_ptr_dtor(&retval);
221 return result;
222 } else {
223 return FAILURE;
224 }
225 }
226 /* }}} */
227
zend_std_call_unsetter(zval * object,zval * member)228 static void zend_std_call_unsetter(zval *object, zval *member) /* {{{ */
229 {
230 zend_class_entry *ce = Z_OBJCE_P(object);
231
232 /* __unset handler is called with one argument:
233 property name
234 */
235
236 if (Z_REFCOUNTED_P(member)) Z_ADDREF_P(member);
237
238 zend_call_method_with_1_params(object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
239
240 zval_ptr_dtor(member);
241 }
242 /* }}} */
243
zend_std_call_issetter(zval * object,zval * member,zval * retval)244 static void zend_std_call_issetter(zval *object, zval *member, zval *retval) /* {{{ */
245 {
246 zend_class_entry *ce = Z_OBJCE_P(object);
247
248 /* __isset handler is called with one argument:
249 property name
250
251 it should return whether the property is set or not
252 */
253
254 if (Z_REFCOUNTED_P(member)) Z_ADDREF_P(member);
255
256 zend_call_method_with_1_params(object, ce, &ce->__isset, ZEND_ISSET_FUNC_NAME, retval, member);
257
258 zval_ptr_dtor(member);
259 }
260 /* }}} */
261
zend_verify_property_access(zend_property_info * property_info,zend_class_entry * ce)262 static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce) /* {{{ */
263 {
264 if (property_info->flags & ZEND_ACC_PUBLIC) {
265 return 1;
266 } else if (property_info->flags & ZEND_ACC_PRIVATE) {
267 return (ce == EG(scope) || property_info->ce == EG(scope));
268 } else if (property_info->flags & ZEND_ACC_PROTECTED) {
269 return zend_check_protected(property_info->ce, EG(scope));
270 }
271 return 0;
272 }
273 /* }}} */
274
is_derived_class(zend_class_entry * child_class,zend_class_entry * parent_class)275 static zend_always_inline zend_bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
276 {
277 child_class = child_class->parent;
278 while (child_class) {
279 if (child_class == parent_class) {
280 return 1;
281 }
282 child_class = child_class->parent;
283 }
284
285 return 0;
286 }
287 /* }}} */
288
zend_get_property_offset(zend_class_entry * ce,zend_string * member,int silent,void ** cache_slot)289 static zend_always_inline uint32_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot) /* {{{ */
290 {
291 zval *zv;
292 zend_property_info *property_info = NULL;
293 uint32_t flags;
294
295 if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
296 return (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1);
297 }
298
299 if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0')) {
300 if (!silent) {
301 if (ZSTR_LEN(member) == 0) {
302 zend_throw_error(NULL, "Cannot access empty property");
303 } else {
304 zend_throw_error(NULL, "Cannot access property started with '\\0'");
305 }
306 }
307 return ZEND_WRONG_PROPERTY_OFFSET;
308 }
309
310 if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
311 goto exit_dynamic;
312 }
313
314 zv = zend_hash_find(&ce->properties_info, member);
315 if (EXPECTED(zv != NULL)) {
316 property_info = (zend_property_info*)Z_PTR_P(zv);
317 flags = property_info->flags;
318 if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
319 /* if it's a shadow - go to access it's private */
320 property_info = NULL;
321 } else {
322 if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
323 if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
324 || UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
325 if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
326 if (!silent) {
327 zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
328 }
329 return ZEND_DYNAMIC_PROPERTY_OFFSET;
330 }
331 goto exit;
332 }
333 } else {
334 /* Try to look in the scope instead */
335 property_info = ZEND_WRONG_PROPERTY_INFO;
336 }
337 }
338 }
339
340 if (EG(scope) != ce
341 && EG(scope)
342 && is_derived_class(ce, EG(scope))
343 && (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
344 && ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
345 property_info = (zend_property_info*)Z_PTR_P(zv);
346 if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) != 0)) {
347 return ZEND_DYNAMIC_PROPERTY_OFFSET;
348 }
349 } else if (UNEXPECTED(property_info == NULL)) {
350 exit_dynamic:
351 if (cache_slot) {
352 CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)ZEND_DYNAMIC_PROPERTY_OFFSET);
353 }
354 return ZEND_DYNAMIC_PROPERTY_OFFSET;
355 } else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
356 /* Information was available, but we were denied access. Error out. */
357 if (!silent) {
358 zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
359 }
360 return ZEND_WRONG_PROPERTY_OFFSET;
361 }
362
363 exit:
364 if (cache_slot) {
365 CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(intptr_t)property_info->offset);
366 }
367 return property_info->offset;
368 }
369 /* }}} */
370
zend_get_property_info(zend_class_entry * ce,zend_string * member,int silent)371 ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent) /* {{{ */
372 {
373 zval *zv;
374 zend_property_info *property_info = NULL;
375 uint32_t flags;
376
377 if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0')) {
378 if (!silent) {
379 if (ZSTR_LEN(member) == 0) {
380 zend_throw_error(NULL, "Cannot access empty property");
381 } else {
382 zend_throw_error(NULL, "Cannot access property started with '\\0'");
383 }
384 }
385 return ZEND_WRONG_PROPERTY_INFO;
386 }
387
388 if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)) {
389 goto exit_dynamic;
390 }
391
392 zv = zend_hash_find(&ce->properties_info, member);
393 if (EXPECTED(zv != NULL)) {
394 property_info = (zend_property_info*)Z_PTR_P(zv);
395 flags = property_info->flags;
396 if (UNEXPECTED((flags & ZEND_ACC_SHADOW) != 0)) {
397 /* if it's a shadow - go to access it's private */
398 property_info = NULL;
399 } else {
400 if (EXPECTED(zend_verify_property_access(property_info, ce) != 0)) {
401 if (UNEXPECTED(!(flags & ZEND_ACC_CHANGED))
402 || UNEXPECTED((flags & ZEND_ACC_PRIVATE))) {
403 if (UNEXPECTED((flags & ZEND_ACC_STATIC) != 0)) {
404 if (!silent) {
405 zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
406 }
407 }
408 goto exit;
409 }
410 } else {
411 /* Try to look in the scope instead */
412 property_info = ZEND_WRONG_PROPERTY_INFO;
413 }
414 }
415 }
416
417 if (EG(scope) != ce
418 && EG(scope)
419 && is_derived_class(ce, EG(scope))
420 && (zv = zend_hash_find(&EG(scope)->properties_info, member)) != NULL
421 && ((zend_property_info*)Z_PTR_P(zv))->flags & ZEND_ACC_PRIVATE) {
422 property_info = (zend_property_info*)Z_PTR_P(zv);
423 } else if (UNEXPECTED(property_info == NULL)) {
424 exit_dynamic:
425 return NULL;
426 } else if (UNEXPECTED(property_info == ZEND_WRONG_PROPERTY_INFO)) {
427 /* Information was available, but we were denied access. Error out. */
428 if (!silent) {
429 zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
430 }
431 return ZEND_WRONG_PROPERTY_INFO;
432 }
433
434 exit:
435 return property_info;
436 }
437 /* }}} */
438
zend_check_property_access(zend_object * zobj,zend_string * prop_info_name)439 ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name) /* {{{ */
440 {
441 zend_property_info *property_info;
442 const char *class_name = NULL;
443 const char *prop_name;
444 zend_string *member;
445 size_t prop_name_len;
446
447 if (ZSTR_VAL(prop_info_name)[0] == 0) {
448 zend_unmangle_property_name_ex(prop_info_name, &class_name, &prop_name, &prop_name_len);
449 member = zend_string_init(prop_name, prop_name_len, 0);
450 } else {
451 member = zend_string_copy(prop_info_name);
452 }
453 property_info = zend_get_property_info(zobj->ce, member, 1);
454 zend_string_release(member);
455 if (property_info == NULL) {
456 /* undefined public property */
457 if (class_name && class_name[0] != '*') {
458 /* we we're looking for a private prop */
459 return FAILURE;
460 }
461 return SUCCESS;
462 } else if (property_info == ZEND_WRONG_PROPERTY_INFO) {
463 return FAILURE;
464 }
465 if (class_name && class_name[0] != '*') {
466 if (!(property_info->flags & ZEND_ACC_PRIVATE)) {
467 /* we we're looking for a private prop but found a non private one of the same name */
468 return FAILURE;
469 } else if (strcmp(ZSTR_VAL(prop_info_name)+1, ZSTR_VAL(property_info->name)+1)) {
470 /* we we're looking for a private prop but found a private one of the same name but another class */
471 return FAILURE;
472 }
473 }
474 return zend_verify_property_access(property_info, zobj->ce) ? SUCCESS : FAILURE;
475 }
476 /* }}} */
477
zend_property_guard_dtor(zval * el)478 static void zend_property_guard_dtor(zval *el) /* {{{ */ {
479 efree_size(Z_PTR_P(el), sizeof(zend_ulong));
480 }
481 /* }}} */
482
zend_get_property_guard(zend_object * zobj,zend_string * member)483 static zend_long *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */
484 {
485 HashTable *guards;
486 zend_long stub, *guard;
487
488 ZEND_ASSERT(GC_FLAGS(zobj) & IS_OBJ_USE_GUARDS);
489 if (GC_FLAGS(zobj) & IS_OBJ_HAS_GUARDS) {
490 guards = Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]);
491 ZEND_ASSERT(guards != NULL);
492 if ((guard = (zend_long *)zend_hash_find_ptr(guards, member)) != NULL) {
493 return guard;
494 }
495 } else {
496 ALLOC_HASHTABLE(guards);
497 zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0);
498 Z_PTR(zobj->properties_table[zobj->ce->default_properties_count]) = guards;
499 GC_FLAGS(zobj) |= IS_OBJ_HAS_GUARDS;
500 }
501
502 stub = 0;
503 return (zend_long *)zend_hash_add_mem(guards, member, &stub, sizeof(zend_ulong));
504 }
505 /* }}} */
506
zend_std_read_property(zval * object,zval * member,int type,void ** cache_slot,zval * rv)507 zval *zend_std_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
508 {
509 zend_object *zobj;
510 zval tmp_member, tmp_object;
511 zval *retval;
512 uint32_t property_offset;
513 zend_long *guard = NULL;
514
515 zobj = Z_OBJ_P(object);
516
517 ZVAL_UNDEF(&tmp_member);
518 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
519 ZVAL_STR(&tmp_member, zval_get_string(member));
520 member = &tmp_member;
521 cache_slot = NULL;
522 }
523
524 #if DEBUG_OBJECT_HANDLERS
525 fprintf(stderr, "Read object #%d property: %s\n", Z_OBJ_HANDLE_P(object), Z_STRVAL_P(member));
526 #endif
527
528 /* make zend_get_property_info silent if we have getter - we may want to use it */
529 property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot);
530
531 if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
532 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
533 retval = OBJ_PROP(zobj, property_offset);
534 if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
535 goto exit;
536 }
537 } else if (EXPECTED(zobj->properties != NULL)) {
538 retval = zend_hash_find(zobj->properties, Z_STR_P(member));
539 if (EXPECTED(retval)) goto exit;
540 }
541 } else if (UNEXPECTED(EG(exception))) {
542 retval = &EG(uninitialized_zval);
543 goto exit;
544 }
545
546 ZVAL_UNDEF(&tmp_object);
547
548 /* magic isset */
549 if ((type == BP_VAR_IS) && zobj->ce->__isset) {
550 zval tmp_result;
551 guard = zend_get_property_guard(zobj, Z_STR_P(member));
552
553 if (!((*guard) & IN_ISSET)) {
554 if (Z_TYPE(tmp_member) == IS_UNDEF) {
555 ZVAL_COPY(&tmp_member, member);
556 member = &tmp_member;
557 }
558 ZVAL_COPY(&tmp_object, object);
559 ZVAL_UNDEF(&tmp_result);
560
561 *guard |= IN_ISSET;
562 zend_std_call_issetter(&tmp_object, member, &tmp_result);
563 *guard &= ~IN_ISSET;
564
565 if (!zend_is_true(&tmp_result)) {
566 retval = &EG(uninitialized_zval);
567 zval_ptr_dtor(&tmp_object);
568 zval_ptr_dtor(&tmp_result);
569 goto exit;
570 }
571
572 zval_ptr_dtor(&tmp_result);
573 }
574 }
575
576 /* magic get */
577 if (zobj->ce->__get) {
578 if (guard == NULL) {
579 guard = zend_get_property_guard(zobj, Z_STR_P(member));
580 }
581 if (!((*guard) & IN_GET)) {
582 /* have getter - try with it! */
583 if (Z_TYPE(tmp_object) == IS_UNDEF) {
584 ZVAL_COPY(&tmp_object, object);
585 }
586 *guard |= IN_GET; /* prevent circular getting */
587 zend_std_call_getter(&tmp_object, member, rv);
588 *guard &= ~IN_GET;
589
590 if (Z_TYPE_P(rv) != IS_UNDEF) {
591 retval = rv;
592 if (!Z_ISREF_P(rv) &&
593 (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
594 SEPARATE_ZVAL(rv);
595 if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) {
596 zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
597 }
598 }
599 } else {
600 retval = &EG(uninitialized_zval);
601 }
602 zval_ptr_dtor(&tmp_object);
603 goto exit;
604 } else {
605 if (Z_STRVAL_P(member)[0] == '\0') {
606 zval_ptr_dtor(&tmp_object);
607 if (Z_STRLEN_P(member) == 0) {
608 zend_throw_error(NULL, "Cannot access empty property");
609 retval = &EG(uninitialized_zval);
610 goto exit;
611 } else {
612 zend_throw_error(NULL, "Cannot access property started with '\\0'");
613 retval = &EG(uninitialized_zval);
614 goto exit;
615 }
616 }
617 }
618 }
619
620 zval_ptr_dtor(&tmp_object);
621
622 if ((type != BP_VAR_IS)) {
623 zend_error(E_NOTICE,"Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), Z_STRVAL_P(member));
624 }
625 retval = &EG(uninitialized_zval);
626
627 exit:
628 if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
629 zval_ptr_dtor(&tmp_member);
630 }
631
632 return retval;
633 }
634 /* }}} */
635
zend_std_write_property(zval * object,zval * member,zval * value,void ** cache_slot)636 ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
637 {
638 zend_object *zobj;
639 zval tmp_member;
640 zval *variable_ptr;
641 uint32_t property_offset;
642
643 zobj = Z_OBJ_P(object);
644
645 ZVAL_UNDEF(&tmp_member);
646 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
647 ZVAL_STR(&tmp_member, zval_get_string(member));
648 member = &tmp_member;
649 cache_slot = NULL;
650 }
651
652 property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__set != NULL), cache_slot);
653
654 if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
655 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
656 variable_ptr = OBJ_PROP(zobj, property_offset);
657 if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
658 goto found;
659 }
660 } else if (EXPECTED(zobj->properties != NULL)) {
661 if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
662 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
663 GC_REFCOUNT(zobj->properties)--;
664 }
665 zobj->properties = zend_array_dup(zobj->properties);
666 }
667 if ((variable_ptr = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
668 found:
669 zend_assign_to_variable(variable_ptr, value, IS_CV);
670 goto exit;
671 }
672 }
673 } else if (UNEXPECTED(EG(exception))) {
674 goto exit;
675 }
676
677 /* magic set */
678 if (zobj->ce->__set) {
679 zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
680
681 if (!((*guard) & IN_SET)) {
682 zval tmp_object;
683
684 ZVAL_COPY(&tmp_object, object);
685 (*guard) |= IN_SET; /* prevent circular setting */
686 if (zend_std_call_setter(&tmp_object, member, value) != SUCCESS) {
687 /* for now, just ignore it - __set should take care of warnings, etc. */
688 }
689 (*guard) &= ~IN_SET;
690 zval_ptr_dtor(&tmp_object);
691 } else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
692 goto write_std_property;
693 } else {
694 if (Z_STRVAL_P(member)[0] == '\0') {
695 if (Z_STRLEN_P(member) == 0) {
696 zend_throw_error(NULL, "Cannot access empty property");
697 goto exit;
698 } else {
699 zend_throw_error(NULL, "Cannot access property started with '\\0'");
700 goto exit;
701 }
702 }
703 }
704 } else if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
705 zval tmp;
706
707 write_std_property:
708 if (Z_REFCOUNTED_P(value)) {
709 if (Z_ISREF_P(value)) {
710 /* if we assign referenced variable, we should separate it */
711 ZVAL_COPY(&tmp, Z_REFVAL_P(value));
712 value = &tmp;
713 } else {
714 Z_ADDREF_P(value);
715 }
716 }
717 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
718 ZVAL_COPY_VALUE(OBJ_PROP(zobj, property_offset), value);
719 } else {
720 if (!zobj->properties) {
721 rebuild_object_properties(zobj);
722 }
723 zend_hash_add_new(zobj->properties, Z_STR_P(member), value);
724 }
725 }
726
727 exit:
728 if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
729 zval_ptr_dtor(&tmp_member);
730 }
731 }
732 /* }}} */
733
zend_std_read_dimension(zval * object,zval * offset,int type,zval * rv)734 zval *zend_std_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
735 {
736 zend_class_entry *ce = Z_OBJCE_P(object);
737 zval tmp_offset, tmp_object;
738
739 if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
740 if (offset == NULL) {
741 /* [] construct */
742 ZVAL_NULL(&tmp_offset);
743 } else {
744 ZVAL_DEREF(offset);
745 ZVAL_COPY(&tmp_offset, offset);
746 }
747
748 ZVAL_COPY(&tmp_object, object);
749 if (type == BP_VAR_IS) {
750 zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetexists", rv, &tmp_offset);
751 if (UNEXPECTED(Z_ISUNDEF_P(rv))) {
752 zval_ptr_dtor(&tmp_object);
753 zval_ptr_dtor(&tmp_offset);
754 return NULL;
755 }
756 if (!i_zend_is_true(rv)) {
757 zval_ptr_dtor(&tmp_object);
758 zval_ptr_dtor(&tmp_offset);
759 zval_ptr_dtor(rv);
760 return &EG(uninitialized_zval);
761 }
762 zval_ptr_dtor(rv);
763 }
764
765 zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetget", rv, &tmp_offset);
766
767 zval_ptr_dtor(&tmp_object);
768 zval_ptr_dtor(&tmp_offset);
769
770 if (UNEXPECTED(Z_TYPE_P(rv) == IS_UNDEF)) {
771 if (UNEXPECTED(!EG(exception))) {
772 zend_throw_error(NULL, "Undefined offset for object of type %s used as array", ZSTR_VAL(ce->name));
773 }
774 return NULL;
775 }
776 return rv;
777 } else {
778 zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
779 return NULL;
780 }
781 }
782 /* }}} */
783
zend_std_write_dimension(zval * object,zval * offset,zval * value)784 static void zend_std_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
785 {
786 zend_class_entry *ce = Z_OBJCE_P(object);
787 zval tmp_offset, tmp_object;
788
789 if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
790 if (!offset) {
791 ZVAL_NULL(&tmp_offset);
792 } else {
793 ZVAL_DEREF(offset);
794 ZVAL_COPY(&tmp_offset, offset);
795 }
796 ZVAL_COPY(&tmp_object, object);
797 zend_call_method_with_2_params(&tmp_object, ce, NULL, "offsetset", NULL, &tmp_offset, value);
798 zval_ptr_dtor(&tmp_object);
799 zval_ptr_dtor(&tmp_offset);
800 } else {
801 zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
802 }
803 }
804 /* }}} */
805
zend_std_has_dimension(zval * object,zval * offset,int check_empty)806 static int zend_std_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
807 {
808 zend_class_entry *ce = Z_OBJCE_P(object);
809 zval retval, tmp_offset, tmp_object;
810 int result;
811
812 if (EXPECTED(instanceof_function_ex(ce, zend_ce_arrayaccess, 1) != 0)) {
813 ZVAL_DEREF(offset);
814 ZVAL_COPY(&tmp_offset, offset);
815 ZVAL_COPY(&tmp_object, object);
816 zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetexists", &retval, &tmp_offset);
817 if (EXPECTED(Z_TYPE(retval) != IS_UNDEF)) {
818 result = i_zend_is_true(&retval);
819 zval_ptr_dtor(&retval);
820 if (check_empty && result && EXPECTED(!EG(exception))) {
821 zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetget", &retval, &tmp_offset);
822 if (EXPECTED(Z_TYPE(retval) != IS_UNDEF)) {
823 result = i_zend_is_true(&retval);
824 zval_ptr_dtor(&retval);
825 }
826 }
827 } else {
828 result = 0;
829 }
830 zval_ptr_dtor(&tmp_object);
831 zval_ptr_dtor(&tmp_offset);
832 } else {
833 zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
834 return 0;
835 }
836 return result;
837 }
838 /* }}} */
839
zend_std_get_property_ptr_ptr(zval * object,zval * member,int type,void ** cache_slot)840 static zval *zend_std_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
841 {
842 zend_object *zobj;
843 zend_string *name;
844 zval *retval = NULL;
845 uint32_t property_offset;
846
847 zobj = Z_OBJ_P(object);
848 if (EXPECTED(Z_TYPE_P(member) == IS_STRING)) {
849 name = Z_STR_P(member);
850 } else {
851 name = zval_get_string(member);
852 }
853
854 #if DEBUG_OBJECT_HANDLERS
855 fprintf(stderr, "Ptr object #%d property: %s\n", Z_OBJ_HANDLE_P(object), ZSTR_VAL(name));
856 #endif
857
858 property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot);
859
860 if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
861 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
862 retval = OBJ_PROP(zobj, property_offset);
863 if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
864 if (EXPECTED(!zobj->ce->__get) ||
865 UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
866 ZVAL_NULL(retval);
867 /* Notice is thrown after creation of the property, to avoid EG(std_property_info)
868 * being overwritten in an error handler. */
869 if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
870 zend_error(E_NOTICE, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
871 }
872 } else {
873 /* we do have getter - fail and let it try again with usual get/set */
874 retval = NULL;
875 }
876 }
877 } else {
878 if (EXPECTED(zobj->properties)) {
879 if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
880 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
881 GC_REFCOUNT(zobj->properties)--;
882 }
883 zobj->properties = zend_array_dup(zobj->properties);
884 }
885 if (EXPECTED((retval = zend_hash_find(zobj->properties, name)) != NULL)) {
886 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
887 zend_string_release(name);
888 }
889 return retval;
890 }
891 }
892 if (EXPECTED(!zobj->ce->__get) ||
893 UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
894 if (UNEXPECTED(!zobj->properties)) {
895 rebuild_object_properties(zobj);
896 }
897 retval = zend_hash_update(zobj->properties, name, &EG(uninitialized_zval));
898 /* Notice is thrown after creation of the property, to avoid EG(std_property_info)
899 * being overwritten in an error handler. */
900 if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
901 zend_error(E_NOTICE, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
902 }
903 }
904 }
905 }
906
907 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
908 zend_string_release(name);
909 }
910 return retval;
911 }
912 /* }}} */
913
zend_std_unset_property(zval * object,zval * member,void ** cache_slot)914 static void zend_std_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
915 {
916 zend_object *zobj;
917 zval tmp_member;
918 uint32_t property_offset;
919
920 zobj = Z_OBJ_P(object);
921
922 ZVAL_UNDEF(&tmp_member);
923 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
924 ZVAL_STR(&tmp_member, zval_get_string(member));
925 member = &tmp_member;
926 cache_slot = NULL;
927 }
928
929 property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), (zobj->ce->__unset != NULL), cache_slot);
930
931 if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
932 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
933 zval *slot = OBJ_PROP(zobj, property_offset);
934
935 if (Z_TYPE_P(slot) != IS_UNDEF) {
936 zval_ptr_dtor(slot);
937 ZVAL_UNDEF(slot);
938 if (zobj->properties) {
939 zobj->properties->u.v.flags |= HASH_FLAG_HAS_EMPTY_IND;
940 }
941 goto exit;
942 }
943 } else if (EXPECTED(zobj->properties != NULL)) {
944 if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
945 if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
946 GC_REFCOUNT(zobj->properties)--;
947 }
948 zobj->properties = zend_array_dup(zobj->properties);
949 }
950 if (EXPECTED(zend_hash_del(zobj->properties, Z_STR_P(member)) != FAILURE)) {
951 goto exit;
952 }
953 }
954 } else if (UNEXPECTED(EG(exception))) {
955 goto exit;
956 }
957
958 /* magic unset */
959 if (zobj->ce->__unset) {
960 zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
961 if (!((*guard) & IN_UNSET)) {
962 zval tmp_object;
963
964 /* have unseter - try with it! */
965 ZVAL_COPY(&tmp_object, object);
966 (*guard) |= IN_UNSET; /* prevent circular unsetting */
967 zend_std_call_unsetter(&tmp_object, member);
968 (*guard) &= ~IN_UNSET;
969 zval_ptr_dtor(&tmp_object);
970 } else {
971 if (Z_STRVAL_P(member)[0] == '\0') {
972 if (Z_STRLEN_P(member) == 0) {
973 zend_throw_error(NULL, "Cannot access empty property");
974 goto exit;
975 } else {
976 zend_throw_error(NULL, "Cannot access property started with '\\0'");
977 goto exit;
978 }
979 }
980 }
981 }
982
983 exit:
984 if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
985 zval_ptr_dtor(&tmp_member);
986 }
987 }
988 /* }}} */
989
zend_std_unset_dimension(zval * object,zval * offset)990 static void zend_std_unset_dimension(zval *object, zval *offset) /* {{{ */
991 {
992 zend_class_entry *ce = Z_OBJCE_P(object);
993 zval tmp_offset, tmp_object;
994
995 if (instanceof_function_ex(ce, zend_ce_arrayaccess, 1)) {
996 ZVAL_DEREF(offset);
997 ZVAL_COPY(&tmp_offset, offset);
998 ZVAL_COPY(&tmp_object, object);
999 zend_call_method_with_1_params(&tmp_object, ce, NULL, "offsetunset", NULL, &tmp_offset);
1000 zval_ptr_dtor(&tmp_object);
1001 zval_ptr_dtor(&tmp_offset);
1002 } else {
1003 zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
1004 }
1005 }
1006 /* }}} */
1007
1008 /* Ensures that we're allowed to call a private method.
1009 * Returns the function address that should be called, or NULL
1010 * if no such function exists.
1011 */
zend_check_private_int(zend_function * fbc,zend_class_entry * ce,zend_string * function_name)1012 static inline zend_function *zend_check_private_int(zend_function *fbc, zend_class_entry *ce, zend_string *function_name) /* {{{ */
1013 {
1014 zval *func;
1015
1016 if (!ce) {
1017 return 0;
1018 }
1019
1020 /* We may call a private function if:
1021 * 1. The class of our object is the same as the scope, and the private
1022 * function (EX(fbc)) has the same scope.
1023 * 2. One of our parent classes are the same as the scope, and it contains
1024 * a private function with the same name that has the same scope.
1025 */
1026 if (fbc->common.scope == ce && EG(scope) == ce) {
1027 /* rule #1 checks out ok, allow the function call */
1028 return fbc;
1029 }
1030
1031
1032 /* Check rule #2 */
1033 ce = ce->parent;
1034 while (ce) {
1035 if (ce == EG(scope)) {
1036 if ((func = zend_hash_find(&ce->function_table, function_name))) {
1037 fbc = Z_FUNC_P(func);
1038 if (fbc->common.fn_flags & ZEND_ACC_PRIVATE
1039 && fbc->common.scope == EG(scope)) {
1040 return fbc;
1041 }
1042 }
1043 break;
1044 }
1045 ce = ce->parent;
1046 }
1047 return NULL;
1048 }
1049 /* }}} */
1050
zend_check_private(zend_function * fbc,zend_class_entry * ce,zend_string * function_name)1051 ZEND_API int zend_check_private(zend_function *fbc, zend_class_entry *ce, zend_string *function_name) /* {{{ */
1052 {
1053 return zend_check_private_int(fbc, ce, function_name) != NULL;
1054 }
1055 /* }}} */
1056
1057 /* Ensures that we're allowed to call a protected method.
1058 */
zend_check_protected(zend_class_entry * ce,zend_class_entry * scope)1059 ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
1060 {
1061 zend_class_entry *fbc_scope = ce;
1062
1063 /* Is the context that's calling the function, the same as one of
1064 * the function's parents?
1065 */
1066 while (fbc_scope) {
1067 if (fbc_scope==scope) {
1068 return 1;
1069 }
1070 fbc_scope = fbc_scope->parent;
1071 }
1072
1073 /* Is the function's scope the same as our current object context,
1074 * or any of the parents of our context?
1075 */
1076 while (scope) {
1077 if (scope==ce) {
1078 return 1;
1079 }
1080 scope = scope->parent;
1081 }
1082 return 0;
1083 }
1084 /* }}} */
1085
zend_get_call_trampoline_func(zend_class_entry * ce,zend_string * method_name,int is_static)1086 ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */
1087 {
1088 size_t mname_len;
1089 zend_op_array *func;
1090 zend_function *fbc = is_static ? ce->__callstatic : ce->__call;
1091
1092 ZEND_ASSERT(fbc);
1093
1094 if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
1095 func = &EG(trampoline).op_array;
1096 } else {
1097 func = ecalloc(1, sizeof(zend_op_array));
1098 }
1099
1100 func->type = ZEND_USER_FUNCTION;
1101 func->arg_flags[0] = 0;
1102 func->arg_flags[1] = 0;
1103 func->arg_flags[2] = 0;
1104 func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC;
1105 if (is_static) {
1106 func->fn_flags |= ZEND_ACC_STATIC;
1107 }
1108 func->this_var = -1;
1109 func->opcodes = &EG(call_trampoline_op);
1110
1111 func->prototype = fbc;
1112 func->scope = fbc->common.scope;
1113 /* reserve space for arguments, local and temorary variables */
1114 func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2;
1115 func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : ZSTR_EMPTY_ALLOC();
1116 func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0;
1117 func->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0;
1118
1119 //??? keep compatibility for "\0" characters
1120 //??? see: Zend/tests/bug46238.phpt
1121 if (UNEXPECTED((mname_len = strlen(ZSTR_VAL(method_name))) != ZSTR_LEN(method_name))) {
1122 func->function_name = zend_string_init(ZSTR_VAL(method_name), mname_len, 0);
1123 } else {
1124 func->function_name = zend_string_copy(method_name);
1125 }
1126
1127 return (zend_function*)func;
1128 }
1129 /* }}} */
1130
zend_get_user_call_function(zend_class_entry * ce,zend_string * method_name)1131 static zend_always_inline zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
1132 {
1133 return zend_get_call_trampoline_func(ce, method_name, 0);
1134 }
1135 /* }}} */
1136
zend_std_get_method(zend_object ** obj_ptr,zend_string * method_name,const zval * key)1137 static union _zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
1138 {
1139 zend_object *zobj = *obj_ptr;
1140 zval *func;
1141 zend_function *fbc;
1142 zend_string *lc_method_name;
1143 ALLOCA_FLAG(use_heap);
1144
1145 if (EXPECTED(key != NULL)) {
1146 lc_method_name = Z_STR_P(key);
1147 #ifdef ZEND_ALLOCA_MAX_SIZE
1148 use_heap = 0;
1149 #endif
1150 } else {
1151 ZSTR_ALLOCA_ALLOC(lc_method_name, ZSTR_LEN(method_name), use_heap);
1152 zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
1153 }
1154
1155 if (UNEXPECTED((func = zend_hash_find(&zobj->ce->function_table, lc_method_name)) == NULL)) {
1156 if (UNEXPECTED(!key)) {
1157 ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
1158 }
1159 if (zobj->ce->__call) {
1160 return zend_get_user_call_function(zobj->ce, method_name);
1161 } else {
1162 return NULL;
1163 }
1164 }
1165
1166 fbc = Z_FUNC_P(func);
1167 /* Check access level */
1168 if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1169 zend_function *updated_fbc;
1170
1171 /* Ensure that if we're calling a private function, we're allowed to do so.
1172 * If we're not and __call() handler exists, invoke it, otherwise error out.
1173 */
1174 updated_fbc = zend_check_private_int(fbc, zobj->ce, lc_method_name);
1175 if (EXPECTED(updated_fbc != NULL)) {
1176 fbc = updated_fbc;
1177 } else {
1178 if (zobj->ce->__call) {
1179 fbc = zend_get_user_call_function(zobj->ce, method_name);
1180 } else {
1181 zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
1182 fbc = NULL;
1183 }
1184 }
1185 } else {
1186 /* Ensure that we haven't overridden a private function and end up calling
1187 * the overriding public function...
1188 */
1189 if (EG(scope) &&
1190 is_derived_class(fbc->common.scope, EG(scope)) &&
1191 fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
1192 if ((func = zend_hash_find(&EG(scope)->function_table, lc_method_name)) != NULL) {
1193 zend_function *priv_fbc = Z_FUNC_P(func);
1194 if (priv_fbc->common.fn_flags & ZEND_ACC_PRIVATE
1195 && priv_fbc->common.scope == EG(scope)) {
1196 fbc = priv_fbc;
1197 }
1198 }
1199 }
1200 if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
1201 /* Ensure that if we're calling a protected function, we're allowed to do so.
1202 * If we're not and __call() handler exists, invoke it, otherwise error out.
1203 */
1204 if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) {
1205 if (zobj->ce->__call) {
1206 fbc = zend_get_user_call_function(zobj->ce, method_name);
1207 } else {
1208 zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
1209 fbc = NULL;
1210 }
1211 }
1212 }
1213 }
1214
1215 if (UNEXPECTED(!key)) {
1216 ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
1217 }
1218 return fbc;
1219 }
1220 /* }}} */
1221
zend_get_user_callstatic_function(zend_class_entry * ce,zend_string * method_name)1222 static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
1223 {
1224 return zend_get_call_trampoline_func(ce, method_name, 1);
1225 }
1226 /* }}} */
1227
zend_std_get_static_method(zend_class_entry * ce,zend_string * function_name,const zval * key)1228 ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name, const zval *key) /* {{{ */
1229 {
1230 zend_function *fbc = NULL;
1231 char *lc_class_name;
1232 zend_string *lc_function_name;
1233 zend_object *object;
1234
1235 if (EXPECTED(key != NULL)) {
1236 lc_function_name = Z_STR_P(key);
1237 } else {
1238 lc_function_name = zend_string_tolower(function_name);
1239 }
1240
1241 if (ZSTR_LEN(function_name) == ZSTR_LEN(ce->name) && ce->constructor) {
1242 lc_class_name = zend_str_tolower_dup(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name));
1243 /* Only change the method to the constructor if the constructor isn't called __construct
1244 * we check for __ so we can be binary safe for lowering, we should use ZEND_CONSTRUCTOR_FUNC_NAME
1245 */
1246 if (!memcmp(lc_class_name, ZSTR_VAL(lc_function_name), ZSTR_LEN(function_name)) && memcmp(ZSTR_VAL(ce->constructor->common.function_name), "__", sizeof("__") - 1)) {
1247 fbc = ce->constructor;
1248 }
1249 efree(lc_class_name);
1250 }
1251
1252 if (EXPECTED(!fbc)) {
1253 zval *func = zend_hash_find(&ce->function_table, lc_function_name);
1254 if (EXPECTED(func != NULL)) {
1255 fbc = Z_FUNC_P(func);
1256 } else {
1257 if (UNEXPECTED(!key)) {
1258 zend_string_release(lc_function_name);
1259 }
1260 if (ce->__call &&
1261 (object = zend_get_this_object(EG(current_execute_data))) != NULL &&
1262 instanceof_function(object->ce, ce)) {
1263 /* Call the top-level defined __call().
1264 * see: tests/classes/__call_004.phpt */
1265
1266 zend_class_entry *call_ce = object->ce;
1267
1268 while (!call_ce->__call) {
1269 call_ce = call_ce->parent;
1270 }
1271 return zend_get_user_call_function(call_ce, function_name);
1272 } else if (ce->__callstatic) {
1273 return zend_get_user_callstatic_function(ce, function_name);
1274 } else {
1275 return NULL;
1276 }
1277 }
1278 }
1279
1280 #if MBO_0
1281 /* right now this function is used for non static method lookup too */
1282 /* Is the function static */
1283 if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) {
1284 zend_error_noreturn(E_ERROR, "Cannot call non static method %s::%s() without object", ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(fbc->common.function_name));
1285 }
1286 #endif
1287 if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1288 /* No further checks necessary, most common case */
1289 } else if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1290 zend_function *updated_fbc;
1291
1292 /* Ensure that if we're calling a private function, we're allowed to do so.
1293 */
1294 updated_fbc = zend_check_private_int(fbc, EG(scope), lc_function_name);
1295 if (EXPECTED(updated_fbc != NULL)) {
1296 fbc = updated_fbc;
1297 } else {
1298 if (ce->__callstatic) {
1299 fbc = zend_get_user_callstatic_function(ce, function_name);
1300 } else {
1301 zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
1302 fbc = NULL;
1303 }
1304 }
1305 } else if ((fbc->common.fn_flags & ZEND_ACC_PROTECTED)) {
1306 /* Ensure that if we're calling a protected function, we're allowed to do so.
1307 */
1308 if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), EG(scope)))) {
1309 if (ce->__callstatic) {
1310 fbc = zend_get_user_callstatic_function(ce, function_name);
1311 } else {
1312 zend_throw_error(NULL, "Call to %s method %s::%s() from context '%s'", zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(function_name), EG(scope) ? ZSTR_VAL(EG(scope)->name) : "");
1313 fbc = NULL;
1314 }
1315 }
1316 }
1317
1318 if (UNEXPECTED(!key)) {
1319 zend_string_release(lc_function_name);
1320 }
1321
1322 return fbc;
1323 }
1324 /* }}} */
1325
zend_std_get_static_property(zend_class_entry * ce,zend_string * property_name,zend_bool silent)1326 ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, zend_bool silent) /* {{{ */
1327 {
1328 zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property_name);
1329 zval *ret;
1330
1331 if (UNEXPECTED(property_info == NULL)) {
1332 goto undeclared_property;
1333 }
1334
1335 if (UNEXPECTED(!zend_verify_property_access(property_info, ce))) {
1336 if (!silent) {
1337 zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1338 }
1339 return NULL;
1340 }
1341
1342 if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {
1343 goto undeclared_property;
1344 }
1345
1346 if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
1347 if (UNEXPECTED(zend_update_class_constants(ce)) != SUCCESS) {
1348 return NULL;
1349 }
1350 }
1351 ret = CE_STATIC_MEMBERS(ce) + property_info->offset;
1352
1353 /* check if static properties were destoyed */
1354 if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
1355 undeclared_property:
1356 if (!silent) {
1357 zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1358 }
1359 ret = NULL;
1360 }
1361
1362 return ret;
1363 }
1364 /* }}} */
1365
zend_std_unset_static_property(zend_class_entry * ce,zend_string * property_name)1366 ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */
1367 {
1368 zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
1369 return 0;
1370 }
1371 /* }}} */
1372
zend_std_get_constructor(zend_object * zobj)1373 ZEND_API union _zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */
1374 {
1375 zend_function *constructor = zobj->ce->constructor;
1376
1377 if (constructor) {
1378 if (constructor->op_array.fn_flags & ZEND_ACC_PUBLIC) {
1379 /* No further checks necessary */
1380 } else if (constructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
1381 /* Ensure that if we're calling a private function, we're allowed to do so.
1382 */
1383 if (UNEXPECTED(constructor->common.scope != EG(scope))) {
1384 if (EG(scope)) {
1385 zend_throw_error(NULL, "Call to private %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(EG(scope)->name));
1386 constructor = NULL;
1387 } else {
1388 zend_throw_error(NULL, "Call to private %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
1389 constructor = NULL;
1390 }
1391 }
1392 } else if ((constructor->common.fn_flags & ZEND_ACC_PROTECTED)) {
1393 /* Ensure that if we're calling a protected function, we're allowed to do so.
1394 * Constructors only have prototype if they are defined by an interface but
1395 * it is the compilers responsibility to take care of the prototype.
1396 */
1397 if (UNEXPECTED(!zend_check_protected(zend_get_function_root_class(constructor), EG(scope)))) {
1398 if (EG(scope)) {
1399 zend_throw_error(NULL, "Call to protected %s::%s() from context '%s'", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(EG(scope)->name));
1400 constructor = NULL;
1401 } else {
1402 zend_throw_error(NULL, "Call to protected %s::%s() from invalid context", ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name));
1403 constructor = NULL;
1404 }
1405 }
1406 }
1407 }
1408
1409 return constructor;
1410 }
1411 /* }}} */
1412
zend_std_compare_objects(zval * o1,zval * o2)1413 static int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
1414 {
1415 zend_object *zobj1, *zobj2;
1416
1417 zobj1 = Z_OBJ_P(o1);
1418 zobj2 = Z_OBJ_P(o2);
1419
1420 if (zobj1->ce != zobj2->ce) {
1421 return 1; /* different classes */
1422 }
1423 if (!zobj1->properties && !zobj2->properties) {
1424 zval *p1, *p2, *end;
1425
1426 if (!zobj1->ce->default_properties_count) {
1427 return 0;
1428 }
1429 p1 = zobj1->properties_table;
1430 p2 = zobj2->properties_table;
1431 end = p1 + zobj1->ce->default_properties_count;
1432 Z_OBJ_PROTECT_RECURSION(o1);
1433 Z_OBJ_PROTECT_RECURSION(o2);
1434 do {
1435 if (Z_TYPE_P(p1) != IS_UNDEF) {
1436 if (Z_TYPE_P(p2) != IS_UNDEF) {
1437 zval result;
1438
1439 if (compare_function(&result, p1, p2)==FAILURE) {
1440 Z_OBJ_UNPROTECT_RECURSION(o1);
1441 Z_OBJ_UNPROTECT_RECURSION(o2);
1442 return 1;
1443 }
1444 if (Z_LVAL(result) != 0) {
1445 Z_OBJ_UNPROTECT_RECURSION(o1);
1446 Z_OBJ_UNPROTECT_RECURSION(o2);
1447 return Z_LVAL(result);
1448 }
1449 } else {
1450 Z_OBJ_UNPROTECT_RECURSION(o1);
1451 Z_OBJ_UNPROTECT_RECURSION(o2);
1452 return 1;
1453 }
1454 } else {
1455 if (Z_TYPE_P(p2) != IS_UNDEF) {
1456 Z_OBJ_UNPROTECT_RECURSION(o1);
1457 Z_OBJ_UNPROTECT_RECURSION(o2);
1458 return 1;
1459 }
1460 }
1461 p1++;
1462 p2++;
1463 } while (p1 != end);
1464 Z_OBJ_UNPROTECT_RECURSION(o1);
1465 Z_OBJ_UNPROTECT_RECURSION(o2);
1466 return 0;
1467 } else {
1468 if (!zobj1->properties) {
1469 rebuild_object_properties(zobj1);
1470 }
1471 if (!zobj2->properties) {
1472 rebuild_object_properties(zobj2);
1473 }
1474 return zend_compare_symbol_tables(zobj1->properties, zobj2->properties);
1475 }
1476 }
1477 /* }}} */
1478
zend_std_has_property(zval * object,zval * member,int has_set_exists,void ** cache_slot)1479 static int zend_std_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
1480 {
1481 zend_object *zobj;
1482 int result;
1483 zval *value = NULL;
1484 zval tmp_member;
1485 uint32_t property_offset;
1486
1487 zobj = Z_OBJ_P(object);
1488
1489 ZVAL_UNDEF(&tmp_member);
1490 if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)) {
1491 ZVAL_STR(&tmp_member, zval_get_string(member));
1492 member = &tmp_member;
1493 cache_slot = NULL;
1494 }
1495
1496 property_offset = zend_get_property_offset(zobj->ce, Z_STR_P(member), 1, cache_slot);
1497
1498 if (EXPECTED(property_offset != ZEND_WRONG_PROPERTY_OFFSET)) {
1499 if (EXPECTED(property_offset != ZEND_DYNAMIC_PROPERTY_OFFSET)) {
1500 value = OBJ_PROP(zobj, property_offset);
1501 if (Z_TYPE_P(value) != IS_UNDEF) {
1502 goto found;
1503 }
1504 } else if (EXPECTED(zobj->properties != NULL) &&
1505 (value = zend_hash_find(zobj->properties, Z_STR_P(member))) != NULL) {
1506 found:
1507 switch (has_set_exists) {
1508 case 0:
1509 ZVAL_DEREF(value);
1510 result = (Z_TYPE_P(value) != IS_NULL);
1511 break;
1512 default:
1513 result = zend_is_true(value);
1514 break;
1515 case 2:
1516 result = 1;
1517 break;
1518 }
1519 goto exit;
1520 }
1521 } else if (UNEXPECTED(EG(exception))) {
1522 result = 0;
1523 goto exit;
1524 }
1525
1526 result = 0;
1527 if ((has_set_exists != 2) && zobj->ce->__isset) {
1528 zend_long *guard = zend_get_property_guard(zobj, Z_STR_P(member));
1529
1530 if (!((*guard) & IN_ISSET)) {
1531 zval rv;
1532 zval tmp_object;
1533
1534 /* have issetter - try with it! */
1535 if (Z_TYPE(tmp_member) == IS_UNDEF) {
1536 ZVAL_COPY(&tmp_member, member);
1537 member = &tmp_member;
1538 }
1539 ZVAL_COPY(&tmp_object, object);
1540 (*guard) |= IN_ISSET; /* prevent circular getting */
1541 zend_std_call_issetter(&tmp_object, member, &rv);
1542 if (Z_TYPE(rv) != IS_UNDEF) {
1543 result = zend_is_true(&rv);
1544 zval_ptr_dtor(&rv);
1545 if (has_set_exists && result) {
1546 if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) {
1547 (*guard) |= IN_GET;
1548 zend_std_call_getter(&tmp_object, member, &rv);
1549 (*guard) &= ~IN_GET;
1550 if (Z_TYPE(rv) != IS_UNDEF) {
1551 result = i_zend_is_true(&rv);
1552 zval_ptr_dtor(&rv);
1553 } else {
1554 result = 0;
1555 }
1556 } else {
1557 result = 0;
1558 }
1559 }
1560 }
1561 (*guard) &= ~IN_ISSET;
1562 zval_ptr_dtor(&tmp_object);
1563 }
1564 }
1565
1566 exit:
1567 if (UNEXPECTED(Z_REFCOUNTED(tmp_member))) {
1568 zval_ptr_dtor(&tmp_member);
1569 }
1570 return result;
1571 }
1572 /* }}} */
1573
zend_std_object_get_class_name(const zend_object * zobj)1574 zend_string *zend_std_object_get_class_name(const zend_object *zobj) /* {{{ */
1575 {
1576 return zend_string_copy(zobj->ce->name);
1577 }
1578 /* }}} */
1579
zend_std_cast_object_tostring(zval * readobj,zval * writeobj,int type)1580 ZEND_API int zend_std_cast_object_tostring(zval *readobj, zval *writeobj, int type) /* {{{ */
1581 {
1582 zval retval;
1583 zend_class_entry *ce;
1584
1585 switch (type) {
1586 case IS_STRING:
1587 ce = Z_OBJCE_P(readobj);
1588 if (ce->__tostring &&
1589 (zend_call_method_with_0_params(readobj, ce, &ce->__tostring, "__tostring", &retval) || EG(exception))) {
1590 if (UNEXPECTED(EG(exception) != NULL)) {
1591 zval *msg, ex, rv;
1592 zval_ptr_dtor(&retval);
1593 ZVAL_OBJ(&ex, EG(exception));
1594 EG(exception) = NULL;
1595 msg = zend_read_property(Z_OBJCE(ex), &ex, "message", sizeof("message") - 1, 1, &rv);
1596 if (UNEXPECTED(Z_TYPE_P(msg) != IS_STRING)) {
1597 ZVAL_EMPTY_STRING(&rv);
1598 msg = &rv;
1599 }
1600 zend_error_noreturn(E_ERROR,
1601 "Method %s::__toString() must not throw an exception, caught %s: %s",
1602 ZSTR_VAL(ce->name), ZSTR_VAL(Z_OBJCE(ex)->name), Z_STRVAL_P(msg));
1603 return FAILURE;
1604 }
1605 if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
1606 if (readobj == writeobj) {
1607 zval_ptr_dtor(readobj);
1608 }
1609 ZVAL_COPY_VALUE(writeobj, &retval);
1610 return SUCCESS;
1611 } else {
1612 zval_ptr_dtor(&retval);
1613 if (readobj == writeobj) {
1614 zval_ptr_dtor(readobj);
1615 }
1616 ZVAL_EMPTY_STRING(writeobj);
1617 zend_error(E_RECOVERABLE_ERROR, "Method %s::__toString() must return a string value", ZSTR_VAL(ce->name));
1618 return SUCCESS;
1619 }
1620 }
1621 return FAILURE;
1622 case _IS_BOOL:
1623 ZVAL_BOOL(writeobj, 1);
1624 return SUCCESS;
1625 case IS_LONG:
1626 ce = Z_OBJCE_P(readobj);
1627 zend_error(E_NOTICE, "Object of class %s could not be converted to int", ZSTR_VAL(ce->name));
1628 if (readobj == writeobj) {
1629 zval_dtor(readobj);
1630 }
1631 ZVAL_LONG(writeobj, 1);
1632 return SUCCESS;
1633 case IS_DOUBLE:
1634 ce = Z_OBJCE_P(readobj);
1635 zend_error(E_NOTICE, "Object of class %s could not be converted to float", ZSTR_VAL(ce->name));
1636 if (readobj == writeobj) {
1637 zval_dtor(readobj);
1638 }
1639 ZVAL_DOUBLE(writeobj, 1);
1640 return SUCCESS;
1641 default:
1642 ZVAL_NULL(writeobj);
1643 break;
1644 }
1645 return FAILURE;
1646 }
1647 /* }}} */
1648
zend_std_get_closure(zval * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zend_object ** obj_ptr)1649 int zend_std_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
1650 {
1651 zval *func;
1652 zend_class_entry *ce;
1653
1654 if (Z_TYPE_P(obj) != IS_OBJECT) {
1655 return FAILURE;
1656 }
1657
1658 ce = Z_OBJCE_P(obj);
1659
1660 if ((func = zend_hash_str_find(&ce->function_table, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1)) == NULL) {
1661 return FAILURE;
1662 }
1663 *fptr_ptr = Z_FUNC_P(func);
1664
1665 *ce_ptr = ce;
1666 if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
1667 if (obj_ptr) {
1668 *obj_ptr = NULL;
1669 }
1670 } else {
1671 if (obj_ptr) {
1672 *obj_ptr = Z_OBJ_P(obj);
1673 }
1674 }
1675 return SUCCESS;
1676 }
1677 /* }}} */
1678
1679 ZEND_API zend_object_handlers std_object_handlers = {
1680 0, /* offset */
1681
1682 zend_object_std_dtor, /* free_obj */
1683 zend_objects_destroy_object, /* dtor_obj */
1684 zend_objects_clone_obj, /* clone_obj */
1685
1686 zend_std_read_property, /* read_property */
1687 zend_std_write_property, /* write_property */
1688 zend_std_read_dimension, /* read_dimension */
1689 zend_std_write_dimension, /* write_dimension */
1690 zend_std_get_property_ptr_ptr, /* get_property_ptr_ptr */
1691 NULL, /* get */
1692 NULL, /* set */
1693 zend_std_has_property, /* has_property */
1694 zend_std_unset_property, /* unset_property */
1695 zend_std_has_dimension, /* has_dimension */
1696 zend_std_unset_dimension, /* unset_dimension */
1697 zend_std_get_properties, /* get_properties */
1698 zend_std_get_method, /* get_method */
1699 NULL, /* call_method */
1700 zend_std_get_constructor, /* get_constructor */
1701 zend_std_object_get_class_name, /* get_class_name */
1702 zend_std_compare_objects, /* compare_objects */
1703 zend_std_cast_object_tostring, /* cast_object */
1704 NULL, /* count_elements */
1705 zend_std_get_debug_info, /* get_debug_info */
1706 zend_std_get_closure, /* get_closure */
1707 zend_std_get_gc, /* get_gc */
1708 NULL, /* do_operation */
1709 NULL, /* compare */
1710 };
1711
1712 /*
1713 * Local variables:
1714 * tab-width: 4
1715 * c-basic-offset: 4
1716 * indent-tabs-mode: t
1717 * End:
1718 */
1719