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    uint32_t i;
329    int t = -1;
330
331    if (class_type->get_iterator) {
332        if (class_type->type == ZEND_INTERNAL_CLASS) {
333            /* inheritance ensures the class has necessary userland methods */
334            return SUCCESS;
335        } else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
336            /* c-level get_iterator cannot be changed (exception being only Traversable is implmented) */
337            if (class_type->num_interfaces) {
338                for (i = 0; i < class_type->num_interfaces; i++) {
339                    if (class_type->interfaces[i] == zend_ce_iterator) {
340                        zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
341                                    class_type->name->val,
342                                    interface->name->val,
343                                    zend_ce_iterator->name->val);
344                        return FAILURE;
345                    }
346                    if (class_type->interfaces[i] == zend_ce_traversable) {
347                        t = i;
348                    }
349                }
350            }
351            if (t == -1) {
352                return FAILURE;
353            }
354        }
355    }
356    class_type->iterator_funcs.zf_new_iterator = NULL;
357    class_type->get_iterator = zend_user_it_get_new_iterator;
358    return SUCCESS;
359}
360/* }}} */
361
362/* {{{ zend_implement_iterator */
363static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
364{
365    if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
366        if (class_type->type == ZEND_INTERNAL_CLASS) {
367            /* inheritance ensures the class has the necessary userland methods */
368            return SUCCESS;
369        } else {
370            /* c-level get_iterator cannot be changed */
371            if (class_type->get_iterator == zend_user_it_get_new_iterator) {
372                zend_error(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
373                            class_type->name->val,
374                            interface->name->val,
375                            zend_ce_aggregate->name->val);
376            }
377            return FAILURE;
378        }
379    }
380    class_type->get_iterator = zend_user_it_get_iterator;
381    class_type->iterator_funcs.zf_valid = NULL;
382    class_type->iterator_funcs.zf_current = NULL;
383    class_type->iterator_funcs.zf_key = NULL;
384    class_type->iterator_funcs.zf_next = NULL;
385    class_type->iterator_funcs.zf_rewind = NULL;
386    if (!class_type->iterator_funcs.funcs) {
387        class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
388    }
389    return SUCCESS;
390}
391/* }}} */
392
393/* {{{ zend_implement_arrayaccess */
394static int zend_implement_arrayaccess(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
395{
396#if 0
397    /* get ht from ce */
398    if (ht->read_dimension != zend_std_read_dimension
399    ||  ht->write_dimension != zend_std_write_dimension
400    ||  ht->has_dimension != zend_std_has_dimension
401    ||  ht->unset_dimension != zend_std_unset_dimension) {
402        return FAILURE;
403    }
404#endif
405    return SUCCESS;
406}
407/* }}}*/
408
409/* {{{ zend_user_serialize */
410ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC)
411{
412    zend_class_entry * ce = Z_OBJCE_P(object);
413    zval retval;
414    int result;
415
416    zend_call_method_with_0_params(object, ce, &ce->serialize_func, "serialize", &retval);
417
418
419    if (Z_TYPE(retval) == IS_UNDEF || EG(exception)) {
420        result = FAILURE;
421    } else {
422        switch(Z_TYPE(retval)) {
423        case IS_NULL:
424            /* we could also make this '*buf_len = 0' but this allows to skip variables */
425            zval_ptr_dtor(&retval);
426            return FAILURE;
427        case IS_STRING:
428            *buffer = (unsigned char*)estrndup(Z_STRVAL(retval), Z_STRLEN(retval));
429            *buf_len = Z_STRLEN(retval);
430            result = SUCCESS;
431            break;
432        default: /* failure */
433            result = FAILURE;
434            break;
435        }
436        zval_ptr_dtor(&retval);
437    }
438
439    if (result == FAILURE && !EG(exception)) {
440        zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "%s::serialize() must return a string or NULL", ce->name->val);
441    }
442    return result;
443}
444/* }}} */
445
446/* {{{ zend_user_unserialize */
447ZEND_API int zend_user_unserialize(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data TSRMLS_DC)
448{
449    zval zdata;
450
451    object_init_ex(object, ce);
452
453    ZVAL_STRINGL(&zdata, (char*)buf, buf_len);
454
455    zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, &zdata);
456
457    zval_ptr_dtor(&zdata);
458
459    if (EG(exception)) {
460        return FAILURE;
461    } else {
462        return SUCCESS;
463    }
464}
465/* }}} */
466
467ZEND_API int zend_class_serialize_deny(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
468{
469    zend_class_entry *ce = Z_OBJCE_P(object);
470    zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Serialization of '%s' is not allowed", ce->name->val);
471    return FAILURE;
472}
473/* }}} */
474
475ZEND_API int zend_class_unserialize_deny(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
476{
477    zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Unserialization of '%s' is not allowed", ce->name->val);
478    return FAILURE;
479}
480/* }}} */
481
482/* {{{ zend_implement_serializable */
483static int zend_implement_serializable(zend_class_entry *interface, zend_class_entry *class_type TSRMLS_DC)
484{
485    if (class_type->parent
486        && (class_type->parent->serialize || class_type->parent->unserialize)
487        && !instanceof_function_ex(class_type->parent, zend_ce_serializable, 1 TSRMLS_CC)) {
488        return FAILURE;
489    }
490    if (!class_type->serialize) {
491        class_type->serialize = zend_user_serialize;
492    }
493    if (!class_type->unserialize) {
494        class_type->unserialize = zend_user_unserialize;
495    }
496    return SUCCESS;
497}
498/* }}}*/
499
500/* {{{ function tables */
501const zend_function_entry zend_funcs_aggregate[] = {
502    ZEND_ABSTRACT_ME(iterator, getIterator, NULL)
503    {NULL, NULL, NULL}
504};
505
506const zend_function_entry zend_funcs_iterator[] = {
507    ZEND_ABSTRACT_ME(iterator, current,  NULL)
508    ZEND_ABSTRACT_ME(iterator, next,     NULL)
509    ZEND_ABSTRACT_ME(iterator, key,      NULL)
510    ZEND_ABSTRACT_ME(iterator, valid,    NULL)
511    ZEND_ABSTRACT_ME(iterator, rewind,   NULL)
512    {NULL, NULL, NULL}
513};
514
515const zend_function_entry *zend_funcs_traversable    = NULL;
516
517ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset, 0, 0, 1)
518    ZEND_ARG_INFO(0, offset)
519ZEND_END_ARG_INFO()
520
521ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_get, 0, 0, 1) /* actually this should be return by ref but atm cannot be */
522    ZEND_ARG_INFO(0, offset)
523ZEND_END_ARG_INFO()
524
525ZEND_BEGIN_ARG_INFO_EX(arginfo_arrayaccess_offset_value, 0, 0, 2)
526    ZEND_ARG_INFO(0, offset)
527    ZEND_ARG_INFO(0, value)
528ZEND_END_ARG_INFO()
529
530const zend_function_entry zend_funcs_arrayaccess[] = {
531    ZEND_ABSTRACT_ME(arrayaccess, offsetExists, arginfo_arrayaccess_offset)
532    ZEND_ABSTRACT_ME(arrayaccess, offsetGet,    arginfo_arrayaccess_offset_get)
533    ZEND_ABSTRACT_ME(arrayaccess, offsetSet,    arginfo_arrayaccess_offset_value)
534    ZEND_ABSTRACT_ME(arrayaccess, offsetUnset,  arginfo_arrayaccess_offset)
535    {NULL, NULL, NULL}
536};
537
538ZEND_BEGIN_ARG_INFO(arginfo_serializable_serialize, 0)
539    ZEND_ARG_INFO(0, serialized)
540ZEND_END_ARG_INFO()
541
542const zend_function_entry zend_funcs_serializable[] = {
543    ZEND_ABSTRACT_ME(serializable, serialize,   NULL)
544    ZEND_FENTRY(unserialize, NULL, arginfo_serializable_serialize, ZEND_ACC_PUBLIC|ZEND_ACC_ABSTRACT|ZEND_ACC_CTOR)
545    {NULL, NULL, NULL}
546};
547/* }}} */
548
549#define REGISTER_ITERATOR_INTERFACE(class_name, class_name_str) \
550    {\
551        zend_class_entry ce;\
552        INIT_CLASS_ENTRY(ce, # class_name_str, zend_funcs_ ## class_name) \
553        zend_ce_ ## class_name = zend_register_internal_interface(&ce TSRMLS_CC);\
554        zend_ce_ ## class_name->interface_gets_implemented = zend_implement_ ## class_name;\
555    }
556
557#define REGISTER_ITERATOR_IMPLEMENT(class_name, interface_name) \
558    zend_class_implements(zend_ce_ ## class_name TSRMLS_CC, 1, zend_ce_ ## interface_name)
559
560/* {{{ zend_register_interfaces */
561ZEND_API void zend_register_interfaces(TSRMLS_D)
562{
563    REGISTER_ITERATOR_INTERFACE(traversable, Traversable);
564
565    REGISTER_ITERATOR_INTERFACE(aggregate, IteratorAggregate);
566    REGISTER_ITERATOR_IMPLEMENT(aggregate, traversable);
567
568    REGISTER_ITERATOR_INTERFACE(iterator, Iterator);
569    REGISTER_ITERATOR_IMPLEMENT(iterator, traversable);
570
571    REGISTER_ITERATOR_INTERFACE(arrayaccess, ArrayAccess);
572
573    REGISTER_ITERATOR_INTERFACE(serializable, Serializable)
574}
575/* }}} */
576
577/*
578 * Local variables:
579 * tab-width: 4
580 * c-basic-offset: 4
581 * indent-tabs-mode: t
582 * End:
583 */
584