1/*
2   +----------------------------------------------------------------------+
3   | Zend Engine                                                          |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2014 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: Marcus Boerger <helly@php.net>                              |
16   +----------------------------------------------------------------------+
17*/
18
19/* $Id$ */
20
21#include "zend.h"
22#include "zend_API.h"
23#include "zend_interfaces.h"
24#include "zend_exceptions.h"
25
26ZEND_API zend_class_entry *zend_ce_traversable;
27ZEND_API zend_class_entry *zend_ce_aggregate;
28ZEND_API zend_class_entry *zend_ce_iterator;
29ZEND_API zend_class_entry *zend_ce_arrayaccess;
30ZEND_API zend_class_entry *zend_ce_serializable;
31
32/* {{{ zend_call_method
33 Only returns the returned zval if retval_ptr != NULL */
34ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_function **fn_proxy, const char *function_name, int function_name_len, zval *retval_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC)
35{
36    int result;
37    zend_fcall_info fci;
38    zval retval;
39    HashTable *function_table;
40
41    zval params[2];
42
43    if (param_count > 0) {
44        ZVAL_COPY_VALUE(&params[0], arg1);
45    }
46    if (param_count > 1) {
47        ZVAL_COPY_VALUE(&params[1], arg2);
48    }
49
50    fci.size = sizeof(fci);
51    /*fci.function_table = NULL; will be read form zend_class_entry of object if needed */
52    fci.object = (object && Z_TYPE_P(object) == IS_OBJECT) ? Z_OBJ_P(object) : NULL;
53    ZVAL_STRINGL(&fci.function_name, function_name, function_name_len);
54    fci.retval = retval_ptr ? retval_ptr : &retval;
55    fci.param_count = param_count;
56    fci.params = params;
57    fci.no_separation = 1;
58    fci.symbol_table = NULL;
59
60    if (!fn_proxy && !obj_ce) {
61        /* no interest in caching and no information already present that is
62         * needed later inside zend_call_function. */
63        fci.function_table = !object ? EG(function_table) : NULL;
64        result = zend_call_function(&fci, NULL TSRMLS_CC);
65        zval_ptr_dtor(&fci.function_name);
66    } else {
67        zend_fcall_info_cache fcic;
68
69        fcic.initialized = 1;
70        if (!obj_ce) {
71            obj_ce = object ? Z_OBJCE_P(object) : NULL;
72        }
73        if (obj_ce) {
74            function_table = &obj_ce->function_table;
75        } else {
76            function_table = EG(function_table);
77        }
78        if (!fn_proxy || !*fn_proxy) {
79            if ((fcic.function_handler = zend_hash_find_ptr(function_table, Z_STR(fci.function_name))) == NULL) {
80                /* error at c-level */
81                zend_error(E_CORE_ERROR, "Couldn't find implementation for method %s%s%s", obj_ce ? obj_ce->name->val : "", obj_ce ? "::" : "", function_name);
82            }
83            if (fn_proxy) {
84                *fn_proxy = fcic.function_handler;
85            }
86        } else {
87            fcic.function_handler = *fn_proxy;
88        }
89        fcic.calling_scope = obj_ce;
90        if (object) {
91            fcic.called_scope = Z_OBJCE_P(object);
92        } else if (obj_ce &&
93                   !(EG(current_execute_data) &&
94                     EG(current_execute_data)->called_scope &&
95                     instanceof_function(EG(current_execute_data)->called_scope, obj_ce TSRMLS_CC))) {
96            fcic.called_scope = obj_ce;
97        } else {
98            fcic.called_scope = EG(current_execute_data) ? EG(current_execute_data)->called_scope : NULL;
99        }
100        fcic.object = object ? Z_OBJ_P(object) : NULL;
101        result = zend_call_function(&fci, &fcic TSRMLS_CC);
102        zval_ptr_dtor(&fci.function_name);
103    }
104    if (result == FAILURE) {
105        /* error at c-level */
106        if (!obj_ce) {
107            obj_ce = object ? Z_OBJCE_P(object) : NULL;
108        }
109        if (!EG(exception)) {
110            zend_error(E_CORE_ERROR, "Couldn't execute method %s%s%s", obj_ce ? obj_ce->name->val : "", obj_ce ? "::" : "", function_name);
111        }
112    }
113    /* copy arguments back, they might be changed by references */
114    if (param_count > 0 && Z_ISREF(params[0]) && !Z_ISREF_P(arg1)) {
115        ZVAL_COPY_VALUE(arg1, &params[0]);
116    }
117    if (param_count > 1 && Z_ISREF(params[1]) && !Z_ISREF_P(arg2)) {
118        ZVAL_COPY_VALUE(arg2, &params[1]);
119    }
120    if (!retval_ptr) {
121        zval_ptr_dtor(&retval);
122        return NULL;
123    }
124    return retval_ptr;
125}
126/* }}} */
127
128/* iterator interface, c-level functions used by engine */
129
130/* {{{ zend_user_it_new_iterator */
131ZEND_API void zend_user_it_new_iterator(zend_class_entry *ce, zval *object, zval *retval TSRMLS_DC)
132{
133    zend_call_method_with_0_params(object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", retval);
134}
135/* }}} */
136
137/* {{{ zend_user_it_invalidate_current */
138ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter TSRMLS_DC)
139{
140    zend_user_iterator *iter = (zend_user_iterator*)_iter;
141
142    if (!Z_ISUNDEF(iter->value)) {
143        zval_ptr_dtor(&iter->value);
144        ZVAL_UNDEF(&iter->value);
145    }
146}
147/* }}} */
148
149/* {{{ zend_user_it_dtor */
150static void zend_user_it_dtor(zend_object_iterator *_iter TSRMLS_DC)
151{
152    zend_user_iterator *iter = (zend_user_iterator*)_iter;
153    zval *object = &iter->it.data;
154
155    zend_user_it_invalidate_current(_iter TSRMLS_CC);
156    zval_ptr_dtor(object);
157}
158/* }}} */
159
160/* {{{ zend_user_it_valid */
161ZEND_API int zend_user_it_valid(zend_object_iterator *_iter TSRMLS_DC)
162{
163    if (_iter) {
164        zend_user_iterator *iter = (zend_user_iterator*)_iter;
165        zval *object = &iter->it.data;
166        zval more;
167        int result;
168
169        zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_valid, "valid", &more);
170        if (Z_TYPE(more) != IS_UNDEF) {
171            result = i_zend_is_true(&more TSRMLS_CC);
172            zval_ptr_dtor(&more);
173            return result ? SUCCESS : FAILURE;
174        }
175    }
176    return FAILURE;
177}
178/* }}} */
179
180/* {{{ zend_user_it_get_current_data */
181ZEND_API zval *zend_user_it_get_current_data(zend_object_iterator *_iter TSRMLS_DC)
182{
183    zend_user_iterator *iter = (zend_user_iterator*)_iter;
184    zval *object = &iter->it.data;
185
186    if (Z_ISUNDEF(iter->value)) {
187        zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value);
188    }
189    return &iter->value;
190}
191/* }}} */
192
193/* {{{ zend_user_it_get_current_key_default */
194#if 0
195static int zend_user_it_get_current_key_default(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
196{
197    *int_key = _iter->index;
198    return HASH_KEY_IS_LONG;
199}
200#endif
201/* }}} */
202
203/* {{{ zend_user_it_get_current_key */
204ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key TSRMLS_DC)
205{
206    zend_user_iterator *iter = (zend_user_iterator*)_iter;
207    zval *object = &iter->it.data;
208    zval retval;
209
210    zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval);
211
212    if (Z_TYPE(retval) != IS_UNDEF) {
213        ZVAL_ZVAL(key, &retval, 1, 1);
214    } else {
215        if (!EG(exception)) {
216            zend_error(E_WARNING, "Nothing returned from %s::key()", iter->ce->name->val);
217        }
218
219        ZVAL_LONG(key, 0);
220    }
221}
222/* }}} */
223
224/* {{{ zend_user_it_move_forward */
225ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter TSRMLS_DC)
226{
227    zend_user_iterator *iter = (zend_user_iterator*)_iter;
228    zval *object = &iter->it.data;
229
230    zend_user_it_invalidate_current(_iter TSRMLS_CC);
231    zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL);
232}
233/* }}} */
234
235/* {{{ zend_user_it_rewind */
236ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter TSRMLS_DC)
237{
238    zend_user_iterator *iter = (zend_user_iterator*)_iter;
239    zval *object = &iter->it.data;
240
241    zend_user_it_invalidate_current(_iter TSRMLS_CC);
242    zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL);
243}
244/* }}} */
245
246zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = {
247    zend_user_it_dtor,
248    zend_user_it_valid,
249    zend_user_it_get_current_data,
250    zend_user_it_get_current_key,
251    zend_user_it_move_forward,
252    zend_user_it_rewind,
253    zend_user_it_invalidate_current
254};
255
256/* {{{ zend_user_it_get_iterator */
257static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
258{
259    zend_user_iterator *iterator;
260
261    if (by_ref) {
262        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
263    }
264
265    iterator = emalloc(sizeof(zend_user_iterator));
266
267    zend_iterator_init((zend_object_iterator*)iterator TSRMLS_CC);
268
269    ZVAL_COPY(&iterator->it.data, object);
270    iterator->it.funcs = ce->iterator_funcs.funcs;
271    iterator->ce = Z_OBJCE_P(object);
272    ZVAL_UNDEF(&iterator->value);
273    return (zend_object_iterator*)iterator;
274}
275/* }}} */
276
277/* {{{ zend_user_it_get_new_iterator */
278ZEND_API zend_object_iterator *zend_user_it_get_new_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
279{
280    zval iterator;
281    zend_object_iterator *new_iterator;
282    zend_class_entry *ce_it;
283
284    zend_user_it_new_iterator(ce, object, &iterator TSRMLS_CC);
285    ce_it = (Z_TYPE(iterator) == IS_OBJECT &&
286        Z_OBJ_HT(iterator)->get_class_entry) ? Z_OBJCE(iterator) : NULL;
287
288    if (!ce_it || !ce_it->get_iterator || (ce_it->get_iterator == zend_user_it_get_new_iterator && Z_OBJ(iterator) == Z_OBJ_P(object))) {
289        if (!EG(exception)) {
290            zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Objects returned by %s::getIterator() must be traversable or implement interface Iterator", ce ? ce->name->val : Z_OBJCE_P(object)->name->val);
291        }
292        zval_ptr_dtor(&iterator);
293        return NULL;
294    }
295
296    new_iterator = ce_it->get_iterator(ce_it, &iterator, by_ref TSRMLS_CC);
297    zval_ptr_dtor(&iterator);
298    return new_iterator;
299}
300/* }}} */
301
302/* {{{ zend_implement_traversable */
303static int zend_implement_traversable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
304{
305    /* check that class_type is traversable at c-level or implements at least one of 'aggregate' and 'Iterator' */
306    uint32_t i;
307
308    if (class_type->get_iterator || (class_type->parent && class_type->parent->get_iterator)) {
309        return SUCCESS;
310    }
311    for (i = 0; i < class_type->num_interfaces; i++) {
312        if (class_type->interfaces[i] == zend_ce_aggregate || class_type->interfaces[i] == zend_ce_iterator) {
313            return SUCCESS;
314        }
315    }
316    zend_error(E_CORE_ERROR, "Class %s must implement interface %s as part of either %s or %s",
317        class_type->name->val,
318        zend_ce_traversable->name->val,
319        zend_ce_iterator->name->val,
320        zend_ce_aggregate->name->val);
321    return FAILURE;
322}
323/* }}} */
324
325/* {{{ zend_implement_aggregate */
326static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
327{
328    int i, t = -1;
329
330    if (class_type->get_iterator) {
331        if (class_type->type == ZEND_INTERNAL_CLASS) {
332            /* inheritance ensures the class has necessary userland methods */
333            return SUCCESS;
334        } else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
335            /* c-level get_iterator cannot be changed (exception being only Traversable is implmented) */
336            if (class_type->num_interfaces) {
337                for (i = 0; i < class_type->num_interfaces; i++) {
338                    if (class_type->interfaces[i] == zend_ce_iterator) {
339                        zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
340                                    class_type->name->val,
341                                    interface->name->val,
342                                    zend_ce_iterator->name->val);
343                        return FAILURE;
344                    }
345                    if (class_type->interfaces[i] == zend_ce_traversable) {
346                        t = i;
347                    }
348                }
349            }
350            if (t == -1) {
351                return FAILURE;
352            }
353        }
354    }
355    class_type->iterator_funcs.zf_new_iterator = NULL;
356    class_type->get_iterator = zend_user_it_get_new_iterator;
357    return SUCCESS;
358}
359/* }}} */
360
361/* {{{ zend_implement_iterator */
362static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
363{
364    if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
365        if (class_type->type == ZEND_INTERNAL_CLASS) {
366            /* inheritance ensures the class has the necessary userland methods */
367            return SUCCESS;
368        } else {
369            /* c-level get_iterator cannot be changed */
370            if (class_type->get_iterator == zend_user_it_get_new_iterator) {
371                zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
372                            class_type->name->val,
373                            interface->name->val,
374                            zend_ce_aggregate->name->val);
375            }
376            return FAILURE;
377        }
378    }
379    class_type->get_iterator = zend_user_it_get_iterator;
380    class_type->iterator_funcs.zf_valid = NULL;
381    class_type->iterator_funcs.zf_current = NULL;
382    class_type->iterator_funcs.zf_key = NULL;
383    class_type->iterator_funcs.zf_next = NULL;
384    class_type->iterator_funcs.zf_rewind = NULL;
385    if (!class_type->iterator_funcs.funcs) {
386        class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
387    }
388    return SUCCESS;
389}
390/* }}} */
391
392/* {{{ zend_implement_arrayaccess */
393static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
394{
395#if 0
396    /* get ht from ce */
397    if (ht->read_dimension != zend_std_read_dimension
398    ||  ht->write_dimension != zend_std_write_dimension
399    ||  ht->has_dimension != zend_std_has_dimension
400    ||  ht->unset_dimension != zend_std_unset_dimension) {
401        return FAILURE;
402    }
403#endif
404    return SUCCESS;
405}
406/* }}}*/
407
408/* {{{ zend_user_serialize */
409ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, uint32_t *buf_len, zend_serialize_data *data TSRMLS_DC)
410{
411    zend_class_entry * ce = Z_OBJCE_P(object);
412    zval retval;
413    int result;
414
415    zend_call_method_with_0_params(object, ce, &ce->serialize_func, "serialize", &retval);
416
417
418    if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
419        result = FAILURE;
420    } else {
421        switch(Z_TYPE(retval)) {
422        case IS_NULL:
423            /* we could also make this '*buf_len = 0' but this allows to skip variables */
424            zval_ptr_dtor(&retval);
425            return FAILURE;
426        case IS_STRING:
427            *buffer = (unsigned char*)estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
428            *buf_len = Z_STRLEN(retval);
429            result = SUCCESS;
430            break;
431        default: /* failure */
432            result = FAILURE;
433            break;
434        }
435        zval_ptr_dtor(&retval);
436    }
437
438    if (result == FAILURE && !EG(exception)) {
439        zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "%s::serialize() must return a string or NULL", ce->name->val);
440    }
441    return result;
442}
443/* }}} */
444
445/* {{{ zend_user_unserialize */
446ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, uint32_t buf_len, zend_unserialize_data *data TSRMLS_DC)
447{
448    zval zdata;
449
450    object_init_ex(object, ce);
451
452    ZVAL_STRINGL(&zdata, (char*)buf, buf_len);
453
454    zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, &zdata);
455
456    zval_ptr_dtor(&zdata);
457
458    if (EG(exception)) {
459        return FAILURE;
460    } else {
461        return SUCCESS;
462    }
463}
464/* }}} */
465
466ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, uint32_t *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
467{
468    zend_class_entry *ce = Z_OBJCE_P(object);
469    zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Serialization of '%s' is not allowed", ce->name->val);
470    return FAILURE;
471}
472/* }}} */
473
474ZEND_API int zend_class_unserialize_deny(zval *object, zend_class_entry *ce, const unsigned char *buf, uint32_t buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
475{
476    zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Unserialization of '%s' is not allowed", ce->name->val);
477    return FAILURE;
478}
479/* }}} */
480
481/* {{{ zend_implement_serializable */
482static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
483{
484    if (class_type->parent
485        && (class_type->parent->serialize || class_type->parent->unserialize)
486        && !instanceof_function_ex(class_type->parent, zend_ce_serializable, 1 TSRMLS_CC)) {
487        return FAILURE;
488    }
489    if (!class_type->serialize) {
490        class_type->serialize = zend_user_serialize;
491    }
492    if (!class_type->unserialize) {
493        class_type->unserialize = zend_user_unserialize;
494    }
495    return SUCCESS;
496}
497/* }}}*/
498
499/* {{{ function tables */
500const zend_function_entry zend_funcs_aggregate[] = {
501    ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
502    {NULL, NULL, NULL}
503};
504
505const zend_function_entry zend_funcs_iterator[] = {
506    ZEND_ABSTRACT_ME(iterator, current,  NULL)
507    ZEND_ABSTRACT_ME(iterator, next,     NULL)
508    ZEND_ABSTRACT_ME(iterator, key,      NULL)
509    ZEND_ABSTRACT_ME(iterator, valid,    NULL)
510    ZEND_ABSTRACT_ME(iterator, rewind,   NULL)
511    {NULL, NULL, NULL}
512};
513
514const zend_function_entry *zend_funcs_traversable    = NULL;
515
516ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset, 0, 0, 1)
517    ZEND_ARG_INFO(0, offset)
518ZEND_END_ARG_INFO()
519
520ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_get, 0, 0, 1) /* actually this should be return by ref but atm cannot be */
521    ZEND_ARG_INFO(0, offset)
522ZEND_END_ARG_INFO()
523
524ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_value, 0, 0, 2)
525    ZEND_ARG_INFO(0, offset)
526    ZEND_ARG_INFO(0, value)
527ZEND_END_ARG_INFO()
528
529const zend_function_entry zend_funcs_arrayaccess[] = {
530    ZEND_ABSTRACT_ME(arrayaccess, offsetExists, arginfo_arrayaccess_offset)
531    ZEND_ABSTRACT_ME(arrayaccess, offsetGet,    arginfo_arrayaccess_offset_get)
532    ZEND_ABSTRACT_ME(arrayaccess, offsetSet,    arginfo_arrayaccess_offset_value)
533    ZEND_ABSTRACT_ME(arrayaccess, offsetUnset,  arginfo_arrayaccess_offset)
534    {NULL, NULL, NULL}
535};
536
537ZEND_BEGIN_ARG_INFO(arginfo_serializable_serialize, 0)
538    ZEND_ARG_INFO(0, serialized)
539ZEND_END_ARG_INFO()
540
541const zend_function_entry zend_funcs_serializable[] = {
542    ZEND_ABSTRACT_ME(serializable, serialize,   NULL)
543    ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)
544    {NULL, NULL, NULL}
545};
546/* }}} */
547
548#define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \
549    {\
550        zend_class_entry ce;\
551        INIT_CLASS_ENTRY(ce, # class_name_str, zend_funcs_ ## class_name) \
552        zend_ce_ ## class_name = zend_register_internal_interface(&ce TSRMLS_CC);\
553        zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\
554    }
555
556#define REGISTER_ITERATOR_IMPLEMENT(class_name, interface_name) \
557    zend_class_implements(zend_ce_ ## class_name TSRMLS_CC, 1, zend_ce_ ## interface_name)
558
559/* {{{ zend_register_interfaces */
560ZEND_API void zend_register_interfaces(TSRMLS_D)
561{
562    REGISTER_ITERATOR_INTERFACE(traversable, Traversable);
563
564    REGISTER_ITERATOR_INTERFACE(aggregate, IteratorAggregate);
565    REGISTER_ITERATOR_IMPLEMENT(aggregate, traversable);
566
567    REGISTER_ITERATOR_INTERFACE(iterator, Iterator);
568    REGISTER_ITERATOR_IMPLEMENT(iterator, traversable);
569
570    REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess);
571
572    REGISTER_ITERATOR_INTERFACE(serializable, Serializable)
573}
574/* }}} */
575
576/*
577 * Local variables:
578 * tab-width: 4
579 * c-basic-offset: 4
580 * indent-tabs-mode: t
581 * End:
582 */
583