xref: /PHP-8.0/ext/intl/common/common_enum.cpp (revision f00bcfbb)
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