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