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