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