1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch>      |
14    +----------------------------------------------------------------------+
15  */
16 
17 #include <php.h>
18 #include <zend.h>
19 #include <zend_API.h>
20 
21 #include "resourcebundle/resourcebundle.h"
22 #include "resourcebundle/resourcebundle_class.h"
23 #include "resourcebundle/resourcebundle_iterator.h"
24 
25 /*
26  * Although libicu offers iterator functions, they are not used here: libicu does iterate
27  * irrespective of array indices. Those cannot be recreated afterwards. Arrays as well as tables
28  * can however be accessed by numerical index, with table keys readable ex post.
29  */
30 
31 /* {{{ resourcebundle_iterator_read */
resourcebundle_iterator_read(ResourceBundle_iterator * iterator)32 static void resourcebundle_iterator_read( ResourceBundle_iterator *iterator )
33 {
34 	UErrorCode icuerror = U_ZERO_ERROR;
35 	ResourceBundle_object *rb = iterator->subject;
36 
37 	rb->child = ures_getByIndex( rb->me, iterator->i, rb->child, &icuerror );
38 
39 	if (U_SUCCESS(icuerror)) {
40 		/* ATTN: key extraction must be the first thing to do... rb->child might be reset in read! */
41 		if (iterator->is_table) {
42 			iterator->currentkey = estrdup( ures_getKey( rb->child ) );
43 		}
44 		resourcebundle_extract_value( &iterator->current, rb );
45 	}
46 	else {
47 		// zend_throw_exception( spl_ce_OutOfRangeException, "Running past end of ResourceBundle", 0);
48 		ZVAL_UNDEF(&iterator->current);
49 	}
50 }
51 /* }}} */
52 
53 /* {{{ resourcebundle_iterator_invalidate */
resourcebundle_iterator_invalidate(zend_object_iterator * iter)54 static void resourcebundle_iterator_invalidate( zend_object_iterator *iter )
55 {
56 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
57 
58 	if (!Z_ISUNDEF(iterator->current)) {
59 		zval_ptr_dtor( &iterator->current );
60 		ZVAL_UNDEF(&iterator->current);
61 	}
62 	if (iterator->currentkey) {
63 		efree( iterator->currentkey );
64 		iterator->currentkey = NULL;
65 	}
66 }
67 /* }}} */
68 
69 /* {{{ resourcebundle_iterator_dtor */
resourcebundle_iterator_dtor(zend_object_iterator * iter)70 static void resourcebundle_iterator_dtor( zend_object_iterator *iter )
71 {
72 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
73 	zval                    *object = &iterator->intern.data;
74 
75 	resourcebundle_iterator_invalidate( iter );
76 
77 	zval_ptr_dtor(object);
78 }
79 /* }}} */
80 
81 /* {{{ resourcebundle_iterator_has_more */
resourcebundle_iterator_has_more(zend_object_iterator * iter)82 static int resourcebundle_iterator_has_more( zend_object_iterator *iter )
83 {
84 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
85 	return (iterator->i < iterator->length) ? SUCCESS : FAILURE;
86 }
87 /* }}} */
88 
89 /* {{{ resourcebundle_iterator_current */
resourcebundle_iterator_current(zend_object_iterator * iter)90 static zval *resourcebundle_iterator_current( zend_object_iterator *iter )
91 {
92 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
93 	if (Z_ISUNDEF(iterator->current)) {
94 		resourcebundle_iterator_read( iterator);
95 	}
96 	return &iterator->current;
97 }
98 /* }}} */
99 
100 /* {{{ resourcebundle_iterator_key */
resourcebundle_iterator_key(zend_object_iterator * iter,zval * key)101 static void resourcebundle_iterator_key( zend_object_iterator *iter, zval *key )
102 {
103 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
104 
105 	if (Z_ISUNDEF(iterator->current)) {
106 		resourcebundle_iterator_read( iterator);
107 	}
108 
109 	if (iterator->is_table) {
110 		ZVAL_STRING(key, iterator->currentkey);
111 	} else {
112 		ZVAL_LONG(key, iterator->i);
113 	}
114 }
115 /* }}} */
116 
117 /* {{{ resourcebundle_iterator_step */
resourcebundle_iterator_step(zend_object_iterator * iter)118 static void resourcebundle_iterator_step( zend_object_iterator *iter )
119 {
120 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
121 
122 	iterator->i++;
123 	resourcebundle_iterator_invalidate( iter );
124 }
125 /* }}} */
126 
127 /* {{{ resourcebundle_iterator_has_reset */
resourcebundle_iterator_reset(zend_object_iterator * iter)128 static void resourcebundle_iterator_reset( zend_object_iterator *iter )
129 {
130 	ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
131 
132 	iterator->i = 0;
133 	resourcebundle_iterator_invalidate( iter );
134 }
135 /* }}} */
136 
137 /* {{{ resourcebundle_iterator_funcs */
138 static const zend_object_iterator_funcs resourcebundle_iterator_funcs = {
139 	resourcebundle_iterator_dtor,
140 	resourcebundle_iterator_has_more,
141 	resourcebundle_iterator_current,
142 	resourcebundle_iterator_key,
143 	resourcebundle_iterator_step,
144 	resourcebundle_iterator_reset,
145 	resourcebundle_iterator_invalidate
146 };
147 /* }}} */
148 
149 /* {{{ resourcebundle_get_iterator */
resourcebundle_get_iterator(zend_class_entry * ce,zval * object,int byref)150 zend_object_iterator *resourcebundle_get_iterator( zend_class_entry *ce, zval *object, int byref )
151 {
152 	ResourceBundle_object   *rb = Z_INTL_RESOURCEBUNDLE_P(object );
153 	ResourceBundle_iterator *iterator = emalloc( sizeof( ResourceBundle_iterator ) );
154 
155 	if (byref) {
156 	     php_error( E_ERROR, "ResourceBundle does not support writable iterators" );
157 	}
158 
159 	zend_iterator_init(&iterator->intern);
160 	Z_ADDREF_P(object);
161 	ZVAL_OBJ(&iterator->intern.data, Z_OBJ_P(object));
162 	iterator->intern.funcs = &resourcebundle_iterator_funcs;
163 
164 	iterator->subject = rb;
165 
166 	/* The iterated rb can only be either URES_TABLE or URES_ARRAY
167 	 * All other types are returned as php primitives!
168 	 */
169 	iterator->is_table = (ures_getType( rb->me ) == URES_TABLE);
170 	iterator->length = ures_getSize( rb->me );
171 
172 	ZVAL_UNDEF(&iterator->current);
173 	iterator->currentkey = NULL;
174 	iterator->i = 0;
175 
176 	return (zend_object_iterator *) iterator;
177 }
178 /* }}} */
179