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