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 | https://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 NULL, /* get_gc */
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_OBJ(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(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_ADDREF(&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 PHP_METHOD(IntlIterator, current)
209 {
210 zval *data;
211 INTLITERATOR_METHOD_INIT_VARS;
212
213 if (zend_parse_parameters_none() == FAILURE) {
214 RETURN_THROWS();
215 }
216
217 INTLITERATOR_METHOD_FETCH_OBJECT;
218 data = ii->iterator->funcs->get_current_data(ii->iterator);
219 if (data) {
220 RETURN_COPY_DEREF(data);
221 }
222 }
223
PHP_METHOD(IntlIterator,key)224 PHP_METHOD(IntlIterator, key)
225 {
226 INTLITERATOR_METHOD_INIT_VARS;
227
228 if (zend_parse_parameters_none() == FAILURE) {
229 RETURN_THROWS();
230 }
231
232 INTLITERATOR_METHOD_FETCH_OBJECT;
233
234 if (ii->iterator->funcs->get_current_key) {
235 ii->iterator->funcs->get_current_key(ii->iterator, return_value);
236 } else {
237 RETURN_LONG(ii->iterator->index);
238 }
239 }
240
PHP_METHOD(IntlIterator,next)241 PHP_METHOD(IntlIterator, next)
242 {
243 INTLITERATOR_METHOD_INIT_VARS;
244
245 if (zend_parse_parameters_none() == FAILURE) {
246 RETURN_THROWS();
247 }
248
249 INTLITERATOR_METHOD_FETCH_OBJECT;
250 ii->iterator->funcs->move_forward(ii->iterator);
251 /* foreach also advances the index after the last iteration,
252 * so I see no problem in incrementing the index here unconditionally */
253 ii->iterator->index++;
254 }
255
PHP_METHOD(IntlIterator,rewind)256 PHP_METHOD(IntlIterator, rewind)
257 {
258 INTLITERATOR_METHOD_INIT_VARS;
259
260 if (zend_parse_parameters_none() == FAILURE) {
261 RETURN_THROWS();
262 }
263
264 INTLITERATOR_METHOD_FETCH_OBJECT;
265 if (ii->iterator->funcs->rewind) {
266 ii->iterator->funcs->rewind(ii->iterator);
267 } else {
268 intl_errors_set(INTLITERATOR_ERROR_P(ii), U_UNSUPPORTED_ERROR,
269 "IntlIterator::rewind: rewind not supported", 0);
270 }
271 }
272
PHP_METHOD(IntlIterator,valid)273 PHP_METHOD(IntlIterator, valid)
274 {
275 INTLITERATOR_METHOD_INIT_VARS;
276
277 if (zend_parse_parameters_none() == FAILURE) {
278 RETURN_THROWS();
279 }
280
281 INTLITERATOR_METHOD_FETCH_OBJECT;
282 RETURN_BOOL(ii->iterator->funcs->valid(ii->iterator) == SUCCESS);
283 }
284
intl_register_common_symbols(int module_number)285 U_CFUNC void intl_register_common_symbols(int module_number)
286 {
287 /* Create and register 'IntlIterator' class. */
288 IntlIterator_ce_ptr = register_class_IntlIterator(zend_ce_iterator);
289 IntlIterator_ce_ptr->create_object = IntlIterator_object_create;
290 IntlIterator_ce_ptr->get_iterator = IntlIterator_get_iterator;
291
292 memcpy(&IntlIterator_handlers, &std_object_handlers,
293 sizeof IntlIterator_handlers);
294 IntlIterator_handlers.offset = XtOffsetOf(IntlIterator_object, zo);
295 IntlIterator_handlers.clone_obj = NULL;
296 IntlIterator_handlers.free_obj = IntlIterator_objects_free;
297
298 register_common_symbols(module_number);
299 }
300