xref: /PHP-7.4/ext/intl/common/common_enum.cpp (revision 83804519)
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 const 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_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 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_COPY_DEREF(return_value, data);
223 	}
224 }
225 
PHP_METHOD(IntlIterator,key)226 static PHP_METHOD(IntlIterator, key)
227 {
228 	INTLITERATOR_METHOD_INIT_VARS;
229 
230 	if (zend_parse_parameters_none() == FAILURE) {
231 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
232 			"IntlIterator::key: bad arguments", 0);
233 		return;
234 	}
235 
236 	INTLITERATOR_METHOD_FETCH_OBJECT;
237 
238 	if (ii->iterator->funcs->get_current_key) {
239 		ii->iterator->funcs->get_current_key(ii->iterator, return_value);
240 	} else {
241 		RETURN_LONG(ii->iterator->index);
242 	}
243 }
244 
PHP_METHOD(IntlIterator,next)245 static PHP_METHOD(IntlIterator, next)
246 {
247 	INTLITERATOR_METHOD_INIT_VARS;
248 
249 	if (zend_parse_parameters_none() == FAILURE) {
250 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
251 			"IntlIterator::next: bad arguments", 0);
252 		return;
253 	}
254 
255 	INTLITERATOR_METHOD_FETCH_OBJECT;
256 	ii->iterator->funcs->move_forward(ii->iterator);
257 	/* foreach also advances the index after the last iteration,
258 	 * so I see no problem in incrementing the index here unconditionally */
259 	ii->iterator->index++;
260 }
261 
PHP_METHOD(IntlIterator,rewind)262 static PHP_METHOD(IntlIterator, rewind)
263 {
264 	INTLITERATOR_METHOD_INIT_VARS;
265 
266 	if (zend_parse_parameters_none() == FAILURE) {
267 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
268 			"IntlIterator::rewind: bad arguments", 0);
269 		return;
270 	}
271 
272 	INTLITERATOR_METHOD_FETCH_OBJECT;
273 	if (ii->iterator->funcs->rewind) {
274 		ii->iterator->funcs->rewind(ii->iterator);
275 	} else {
276 		intl_errors_set(INTLITERATOR_ERROR_P(ii), U_UNSUPPORTED_ERROR,
277 			"IntlIterator::rewind: rewind not supported", 0);
278 	}
279 }
280 
PHP_METHOD(IntlIterator,valid)281 static PHP_METHOD(IntlIterator, valid)
282 {
283 	INTLITERATOR_METHOD_INIT_VARS;
284 
285 	if (zend_parse_parameters_none() == FAILURE) {
286 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
287 			"IntlIterator::valid: bad arguments", 0);
288 		return;
289 	}
290 
291 	INTLITERATOR_METHOD_FETCH_OBJECT;
292 	RETURN_BOOL(ii->iterator->funcs->valid(ii->iterator) == SUCCESS);
293 }
294 
295 ZEND_BEGIN_ARG_INFO_EX(ainfo_se_void, 0, 0, 0)
296 ZEND_END_ARG_INFO()
297 
298 static const zend_function_entry IntlIterator_class_functions[] = {
299 	PHP_ME(IntlIterator,	current,	ainfo_se_void,			ZEND_ACC_PUBLIC)
300 	PHP_ME(IntlIterator,	key,		ainfo_se_void,			ZEND_ACC_PUBLIC)
301 	PHP_ME(IntlIterator,	next,		ainfo_se_void,			ZEND_ACC_PUBLIC)
302 	PHP_ME(IntlIterator,	rewind,		ainfo_se_void,			ZEND_ACC_PUBLIC)
303 	PHP_ME(IntlIterator,	valid,		ainfo_se_void,			ZEND_ACC_PUBLIC)
304 	PHP_FE_END
305 };
306 
307 
308 /* {{{ intl_register_IntlIterator_class
309  * Initialize 'IntlIterator' class
310  */
intl_register_IntlIterator_class(void)311 U_CFUNC void intl_register_IntlIterator_class(void)
312 {
313 	zend_class_entry ce;
314 
315 	/* Create and register 'IntlIterator' class. */
316 	INIT_CLASS_ENTRY(ce, "IntlIterator", IntlIterator_class_functions);
317 	ce.create_object = IntlIterator_object_create;
318 	IntlIterator_ce_ptr = zend_register_internal_class(&ce);
319 	IntlIterator_ce_ptr->get_iterator = IntlIterator_get_iterator;
320 	zend_class_implements(IntlIterator_ce_ptr, 1,
321 		zend_ce_iterator);
322 
323 	memcpy(&IntlIterator_handlers, &std_object_handlers,
324 		sizeof IntlIterator_handlers);
325 	IntlIterator_handlers.offset = XtOffsetOf(IntlIterator_object, zo);
326 	IntlIterator_handlers.clone_obj = NULL;
327 	IntlIterator_handlers.dtor_obj = zend_objects_destroy_object;
328 	IntlIterator_handlers.free_obj = IntlIterator_objects_free;
329 
330 }
331