1 /*
2 +----------------------------------------------------------------------+
3 | This source file is subject to version 3.01 of the PHP license, |
4 | that is bundled with this package in the file LICENSE, and is |
5 | available through the world-wide-web at the following url: |
6 | http://www.php.net/license/3_01.txt |
7 | If you did not receive a copy of the PHP license and are unable to |
8 | obtain it through the world-wide-web, please send a note to |
9 | license@php.net so we can mail you a copy immediately. |
10 +----------------------------------------------------------------------+
11 | Authors: Gustavo Lopes <cataphract@php.net> |
12 +----------------------------------------------------------------------+
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #include "../intl_cppshims.h"
20
21 // Fix build on Windows/old versions of ICU
22 #include <stdio.h>
23
24 #include "common_enum.h"
25 #include "common_arginfo.h"
26
27 extern "C" {
28 #include <zend_interfaces.h>
29 #include <zend_exceptions.h>
30 }
31
32 zend_class_entry *IntlIterator_ce_ptr;
33 zend_object_handlers IntlIterator_handlers;
34
zoi_with_current_dtor(zend_object_iterator * iter)35 void zoi_with_current_dtor(zend_object_iterator *iter)
36 {
37 zoi_with_current *zoiwc = (zoi_with_current*)iter;
38
39 if (!Z_ISUNDEF(zoiwc->wrapping_obj)) {
40 /* we have to copy the pointer because zoiwc->wrapping_obj may be
41 * changed midway the execution of zval_ptr_dtor() */
42 zval *zwo = &zoiwc->wrapping_obj;
43
44 /* object is still here, we can rely on it to call this again and
45 * destroy this object */
46 zval_ptr_dtor(zwo);
47 } else {
48 /* Object not here anymore (we've been called by the object free handler)
49 * Note that the iterator wrapper objects (that also depend on this
50 * structure) call this function earlier, in the destruction phase, which
51 * precedes the object free phase. Therefore there's no risk on this
52 * function being called by the iterator wrapper destructor function and
53 * not finding the memory of this iterator allocated anymore. */
54 iter->funcs->invalidate_current(iter);
55 zoiwc->destroy_it(iter);
56 }
57 }
58
zoi_with_current_valid(zend_object_iterator * iter)59 U_CFUNC int zoi_with_current_valid(zend_object_iterator *iter)
60 {
61 return Z_ISUNDEF(((zoi_with_current*)iter)->current)? FAILURE : SUCCESS;
62 }
63
zoi_with_current_get_current_data(zend_object_iterator * iter)64 U_CFUNC zval *zoi_with_current_get_current_data(zend_object_iterator *iter)
65 {
66 return &((zoi_with_current*)iter)->current;
67 }
68
zoi_with_current_invalidate_current(zend_object_iterator * iter)69 U_CFUNC void zoi_with_current_invalidate_current(zend_object_iterator *iter)
70 {
71 zoi_with_current *zoi_iter = (zoi_with_current*)iter;
72 if (!Z_ISUNDEF(zoi_iter->current)) {
73 zval_ptr_dtor(&zoi_iter->current);
74 ZVAL_UNDEF(&zoi_iter->current); //valid would return FAILURE now
75 }
76 }
77
string_enum_current_move_forward(zend_object_iterator * iter)78 static void string_enum_current_move_forward(zend_object_iterator *iter)
79 {
80 zoi_with_current *zoi_iter = (zoi_with_current*)iter;
81 INTLITERATOR_METHOD_INIT_VARS;
82
83 iter->funcs->invalidate_current(iter);
84
85 object = &zoi_iter->wrapping_obj;
86 INTLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
87
88 int32_t result_length;
89 const char *result = ((StringEnumeration*)Z_PTR(iter->data))->next(
90 &result_length, INTLITERATOR_ERROR_CODE(ii));
91
92 intl_error_set_code(NULL, INTLITERATOR_ERROR_CODE(ii));
93 if (U_FAILURE(INTLITERATOR_ERROR_CODE(ii))) {
94 intl_errors_set_custom_msg(INTL_DATA_ERROR_P(ii),
95 "Error fetching next iteration element", 0);
96 } else if (result) {
97 ZVAL_STRINGL(&zoi_iter->current, result, result_length);
98 } //else we've reached the end of the enum, nothing more is required
99 }
100
string_enum_rewind(zend_object_iterator * iter)101 static void string_enum_rewind(zend_object_iterator *iter)
102 {
103 zoi_with_current *zoi_iter = (zoi_with_current*)iter;
104 INTLITERATOR_METHOD_INIT_VARS;
105
106 if (!Z_ISUNDEF(zoi_iter->current)) {
107 iter->funcs->invalidate_current(iter);
108 }
109
110 object = &zoi_iter->wrapping_obj;
111 INTLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
112
113 ((StringEnumeration*)Z_PTR(iter->data))->reset(INTLITERATOR_ERROR_CODE(ii));
114
115 intl_error_set_code(NULL, INTLITERATOR_ERROR_CODE(ii));
116 if (U_FAILURE(INTLITERATOR_ERROR_CODE(ii))) {
117 intl_errors_set_custom_msg(INTL_DATA_ERROR_P(ii),
118 "Error resetting enumeration", 0);
119 } else {
120 iter->funcs->move_forward(iter);
121 }
122 }
123
string_enum_destroy_it(zend_object_iterator * iter)124 static void string_enum_destroy_it(zend_object_iterator *iter)
125 {
126 delete (StringEnumeration*)Z_PTR(iter->data);
127 }
128
129 static const zend_object_iterator_funcs string_enum_object_iterator_funcs = {
130 zoi_with_current_dtor,
131 zoi_with_current_valid,
132 zoi_with_current_get_current_data,
133 NULL,
134 string_enum_current_move_forward,
135 string_enum_rewind,
136 zoi_with_current_invalidate_current
137 };
138
IntlIterator_from_StringEnumeration(StringEnumeration * se,zval * object)139 U_CFUNC void IntlIterator_from_StringEnumeration(StringEnumeration *se, zval *object)
140 {
141 IntlIterator_object *ii;
142 object_init_ex(object, IntlIterator_ce_ptr);
143 ii = Z_INTL_ITERATOR_P(object);
144 ii->iterator = (zend_object_iterator*)emalloc(sizeof(zoi_with_current));
145 zend_iterator_init(ii->iterator);
146 ZVAL_PTR(&ii->iterator->data, se);
147 ii->iterator->funcs = &string_enum_object_iterator_funcs;
148 ii->iterator->index = 0;
149 ((zoi_with_current*)ii->iterator)->destroy_it = string_enum_destroy_it;
150 ZVAL_OBJ(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(object));
151 ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->current);
152 }
153
IntlIterator_objects_free(zend_object * object)154 static void IntlIterator_objects_free(zend_object *object)
155 {
156 IntlIterator_object *ii = php_intl_iterator_fetch_object(object);
157
158 if (ii->iterator) {
159 zval *wrapping_objp = &((zoi_with_current*)ii->iterator)->wrapping_obj;
160 ZVAL_UNDEF(wrapping_objp);
161 zend_iterator_dtor(ii->iterator);
162 }
163 intl_error_reset(INTLITERATOR_ERROR_P(ii));
164
165 zend_object_std_dtor(&ii->zo);
166 }
167
IntlIterator_get_iterator(zend_class_entry * ce,zval * object,int by_ref)168 static zend_object_iterator *IntlIterator_get_iterator(
169 zend_class_entry *ce, zval *object, int by_ref)
170 {
171 if (by_ref) {
172 zend_throw_exception(NULL,
173 "Iteration by reference is not supported", 0);
174 return NULL;
175 }
176
177 IntlIterator_object *ii = Z_INTL_ITERATOR_P(object);
178
179 if (ii->iterator == NULL) {
180 zend_throw_exception(NULL,
181 "The IntlIterator is not properly constructed", 0);
182 return NULL;
183 }
184
185 GC_ADDREF(&ii->iterator->std);
186
187 return ii->iterator;
188 }
189
IntlIterator_object_create(zend_class_entry * ce)190 static zend_object *IntlIterator_object_create(zend_class_entry *ce)
191 {
192 IntlIterator_object *intern;
193
194 intern = (IntlIterator_object*)ecalloc(1, sizeof(IntlIterator_object) + sizeof(zval) * (ce->default_properties_count - 1));
195
196 zend_object_std_init(&intern->zo, ce);
197 object_properties_init(&intern->zo, ce);
198 intl_error_init(INTLITERATOR_ERROR_P(intern));
199
200 intern->iterator = NULL;
201
202 intern->zo.handlers = &IntlIterator_handlers;
203
204 return &intern->zo;
205 }
206
PHP_METHOD(IntlIterator,current)207 PHP_METHOD(IntlIterator, current)
208 {
209 zval *data;
210 INTLITERATOR_METHOD_INIT_VARS;
211
212 if (zend_parse_parameters_none() == FAILURE) {
213 RETURN_THROWS();
214 }
215
216 INTLITERATOR_METHOD_FETCH_OBJECT;
217 data = ii->iterator->funcs->get_current_data(ii->iterator);
218 if (data) {
219 ZVAL_COPY_DEREF(return_value, data);
220 }
221 }
222
PHP_METHOD(IntlIterator,key)223 PHP_METHOD(IntlIterator, key)
224 {
225 INTLITERATOR_METHOD_INIT_VARS;
226
227 if (zend_parse_parameters_none() == FAILURE) {
228 RETURN_THROWS();
229 }
230
231 INTLITERATOR_METHOD_FETCH_OBJECT;
232
233 if (ii->iterator->funcs->get_current_key) {
234 ii->iterator->funcs->get_current_key(ii->iterator, return_value);
235 } else {
236 RETURN_LONG(ii->iterator->index);
237 }
238 }
239
PHP_METHOD(IntlIterator,next)240 PHP_METHOD(IntlIterator, next)
241 {
242 INTLITERATOR_METHOD_INIT_VARS;
243
244 if (zend_parse_parameters_none() == FAILURE) {
245 RETURN_THROWS();
246 }
247
248 INTLITERATOR_METHOD_FETCH_OBJECT;
249 ii->iterator->funcs->move_forward(ii->iterator);
250 /* foreach also advances the index after the last iteration,
251 * so I see no problem in incrementing the index here unconditionally */
252 ii->iterator->index++;
253 }
254
PHP_METHOD(IntlIterator,rewind)255 PHP_METHOD(IntlIterator, rewind)
256 {
257 INTLITERATOR_METHOD_INIT_VARS;
258
259 if (zend_parse_parameters_none() == FAILURE) {
260 RETURN_THROWS();
261 }
262
263 INTLITERATOR_METHOD_FETCH_OBJECT;
264 if (ii->iterator->funcs->rewind) {
265 ii->iterator->funcs->rewind(ii->iterator);
266 } else {
267 intl_errors_set(INTLITERATOR_ERROR_P(ii), U_UNSUPPORTED_ERROR,
268 "IntlIterator::rewind: rewind not supported", 0);
269 }
270 }
271
PHP_METHOD(IntlIterator,valid)272 PHP_METHOD(IntlIterator, valid)
273 {
274 INTLITERATOR_METHOD_INIT_VARS;
275
276 if (zend_parse_parameters_none() == FAILURE) {
277 RETURN_THROWS();
278 }
279
280 INTLITERATOR_METHOD_FETCH_OBJECT;
281 RETURN_BOOL(ii->iterator->funcs->valid(ii->iterator) == SUCCESS);
282 }
283
284 /* {{{ intl_register_IntlIterator_class
285 * Initialize 'IntlIterator' class
286 */
intl_register_IntlIterator_class(void)287 U_CFUNC void intl_register_IntlIterator_class(void)
288 {
289 zend_class_entry ce;
290
291 /* Create and register 'IntlIterator' class. */
292 INIT_CLASS_ENTRY(ce, "IntlIterator", class_IntlIterator_methods);
293 ce.create_object = IntlIterator_object_create;
294 IntlIterator_ce_ptr = zend_register_internal_class(&ce);
295 IntlIterator_ce_ptr->get_iterator = IntlIterator_get_iterator;
296 zend_class_implements(IntlIterator_ce_ptr, 1,
297 zend_ce_iterator);
298
299 memcpy(&IntlIterator_handlers, &std_object_handlers,
300 sizeof IntlIterator_handlers);
301 IntlIterator_handlers.offset = XtOffsetOf(IntlIterator_object, zo);
302 IntlIterator_handlers.clone_obj = NULL;
303 IntlIterator_handlers.dtor_obj = zend_objects_destroy_object;
304 IntlIterator_handlers.free_obj = IntlIterator_objects_free;
305
306 }
307