xref: /PHP-5.5/ext/intl/common/common_enum.cpp (revision fcc6611d)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
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 TSRMLS_DC)36 void zoi_with_current_dtor(zend_object_iterator *iter TSRMLS_DC)
37 {
38 	zoi_with_current *zoiwc = (zoi_with_current*)iter;
39 
40 	if (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 TSRMLS_CC);
56 		zoiwc->destroy_it(iter TSRMLS_CC);
57 		efree(iter);
58 	}
59 }
60 
zoi_with_current_valid(zend_object_iterator * iter TSRMLS_DC)61 U_CFUNC int zoi_with_current_valid(zend_object_iterator *iter TSRMLS_DC)
62 {
63 	return ((zoi_with_current*)iter)->current != NULL ? SUCCESS : FAILURE;
64 }
65 
zoi_with_current_get_current_data(zend_object_iterator * iter,zval *** data TSRMLS_DC)66 U_CFUNC void zoi_with_current_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
67 {
68 	*data = &((zoi_with_current*)iter)->current;
69 }
70 
zoi_with_current_invalidate_current(zend_object_iterator * iter TSRMLS_DC)71 U_CFUNC void zoi_with_current_invalidate_current(zend_object_iterator *iter TSRMLS_DC)
72 {
73 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
74 	if (zoi_iter->current) {
75 		zval_ptr_dtor(&zoi_iter->current);
76 		zoi_iter->current = NULL; //valid would return FAILURE now
77 	}
78 }
79 
string_enum_current_move_forward(zend_object_iterator * iter TSRMLS_DC)80 static void string_enum_current_move_forward(zend_object_iterator *iter TSRMLS_DC)
81 {
82 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
83 	INTLITERATOR_METHOD_INIT_VARS;
84 
85 	iter->funcs->invalidate_current(iter TSRMLS_CC);
86 
87 	object = zoi_iter->wrapping_obj;
88 	INTLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
89 
90 	int32_t result_length;
91 	const char *result = ((StringEnumeration*)iter->data)->next(
92 		&result_length, INTLITERATOR_ERROR_CODE(ii));
93 
94 	intl_error_set_code(NULL, INTLITERATOR_ERROR_CODE(ii) TSRMLS_CC);
95 	if (U_FAILURE(INTLITERATOR_ERROR_CODE(ii))) {
96 		intl_errors_set_custom_msg(INTL_DATA_ERROR_P(ii),
97 			"Error fetching next iteration element", 0 TSRMLS_CC);
98 	} else if (result) {
99 		MAKE_STD_ZVAL(zoi_iter->current);
100 		ZVAL_STRINGL(zoi_iter->current, result, result_length, 1);
101 	} //else we've reached the end of the enum, nothing more is required
102 }
103 
string_enum_rewind(zend_object_iterator * iter TSRMLS_DC)104 static void string_enum_rewind(zend_object_iterator *iter TSRMLS_DC)
105 {
106 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
107 	INTLITERATOR_METHOD_INIT_VARS;
108 
109 	if (zoi_iter->current) {
110 		iter->funcs->invalidate_current(iter TSRMLS_CC);
111 	}
112 
113 	object = zoi_iter->wrapping_obj;
114 	INTLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
115 
116 	((StringEnumeration*)iter->data)->reset(INTLITERATOR_ERROR_CODE(ii));
117 
118 	intl_error_set_code(NULL, INTLITERATOR_ERROR_CODE(ii) TSRMLS_CC);
119 	if (U_FAILURE(INTLITERATOR_ERROR_CODE(ii))) {
120 		intl_errors_set_custom_msg(INTL_DATA_ERROR_P(ii),
121 			"Error resetting enumeration", 0 TSRMLS_CC);
122 	} else {
123 		iter->funcs->move_forward(iter TSRMLS_CC);
124 	}
125 }
126 
string_enum_destroy_it(zend_object_iterator * iter TSRMLS_DC)127 static void string_enum_destroy_it(zend_object_iterator *iter TSRMLS_DC)
128 {
129 	delete (StringEnumeration*)iter->data;
130 }
131 
132 static zend_object_iterator_funcs string_enum_object_iterator_funcs = {
133 	zoi_with_current_dtor,
134 	zoi_with_current_valid,
135 	zoi_with_current_get_current_data,
136 	NULL,
137 	string_enum_current_move_forward,
138 	string_enum_rewind,
139 	zoi_with_current_invalidate_current
140 };
141 
IntlIterator_from_StringEnumeration(StringEnumeration * se,zval * object TSRMLS_DC)142 U_CFUNC void IntlIterator_from_StringEnumeration(StringEnumeration *se, zval *object TSRMLS_DC)
143 {
144 	IntlIterator_object *ii;
145 	object_init_ex(object, IntlIterator_ce_ptr);
146 	ii = (IntlIterator_object*)zend_object_store_get_object(object TSRMLS_CC);
147 	ii->iterator = (zend_object_iterator*)emalloc(sizeof(zoi_with_current));
148 	ii->iterator->data = (void*)se;
149 	ii->iterator->funcs = &string_enum_object_iterator_funcs;
150 	ii->iterator->index = 0;
151 	((zoi_with_current*)ii->iterator)->destroy_it = string_enum_destroy_it;
152 	((zoi_with_current*)ii->iterator)->wrapping_obj = object;
153 	((zoi_with_current*)ii->iterator)->current = NULL;
154 }
155 
IntlIterator_objects_free(zend_object * object TSRMLS_DC)156 static void IntlIterator_objects_free(zend_object *object TSRMLS_DC)
157 {
158 	IntlIterator_object	*ii = (IntlIterator_object*) object;
159 
160 	if (ii->iterator) {
161 		zval **wrapping_objp = &((zoi_with_current*)ii->iterator)->wrapping_obj;
162 		*wrapping_objp = NULL;
163 		ii->iterator->funcs->dtor(ii->iterator TSRMLS_CC);
164 	}
165 	intl_error_reset(INTLITERATOR_ERROR_P(ii) TSRMLS_CC);
166 
167 	zend_object_std_dtor(&ii->zo TSRMLS_CC);
168 
169 	efree(ii);
170 }
171 
IntlIterator_get_iterator(zend_class_entry * ce,zval * object,int by_ref TSRMLS_DC)172 static zend_object_iterator *IntlIterator_get_iterator(
173 	zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
174 {
175 	if (by_ref) {
176 		zend_throw_exception(NULL,
177 			"Iteration by reference is not supported", 0 TSRMLS_CC);
178 		return NULL;
179 	}
180 
181 	IntlIterator_object *ii = (IntlIterator_object*)
182 		zend_object_store_get_object(object TSRMLS_CC);
183 
184 	if (ii->iterator == NULL) {
185 		zend_throw_exception(NULL,
186 			"The IntlIterator is not properly constructed", 0 TSRMLS_CC);
187 		return NULL;
188 	}
189 
190 	zval_add_ref(&object);
191 
192 	return ii->iterator;
193 }
194 
IntlIterator_object_create(zend_class_entry * ce TSRMLS_DC)195 static zend_object_value IntlIterator_object_create(zend_class_entry *ce TSRMLS_DC)
196 {
197 	zend_object_value	retval;
198 	IntlIterator_object	*intern;
199 
200 	intern = (IntlIterator_object*)ecalloc(1, sizeof(IntlIterator_object));
201 
202 	zend_object_std_init(&intern->zo, ce TSRMLS_CC);
203 #if PHP_VERSION_ID < 50399
204     zend_hash_copy(intern->zo.properties, &(ce->default_properties),
205         (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
206 #else
207     object_properties_init((zend_object*) intern, ce);
208 #endif
209 	intl_error_init(INTLITERATOR_ERROR_P(intern) TSRMLS_CC);
210 	intern->iterator = NULL;
211 
212 	retval.handle = zend_objects_store_put(
213 		intern,
214 		(zend_objects_store_dtor_t)zend_objects_destroy_object,
215 		(zend_objects_free_object_storage_t)IntlIterator_objects_free,
216 		NULL TSRMLS_CC);
217 
218 	retval.handlers = &IntlIterator_handlers;
219 
220 	return retval;
221 }
222 
PHP_METHOD(IntlIterator,current)223 static PHP_METHOD(IntlIterator, current)
224 {
225 	zval **data;
226 	INTLITERATOR_METHOD_INIT_VARS;
227 
228 	if (zend_parse_parameters_none() == FAILURE) {
229 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
230 			"IntlIterator::current: bad arguments", 0 TSRMLS_CC);
231 		return;
232 	}
233 
234 	INTLITERATOR_METHOD_FETCH_OBJECT;
235 	ii->iterator->funcs->get_current_data(ii->iterator, &data TSRMLS_CC);
236 	if (data && *data) {
237 		RETURN_ZVAL(*data, 1, 0);
238 	}
239 }
240 
PHP_METHOD(IntlIterator,key)241 static PHP_METHOD(IntlIterator, key)
242 {
243 	INTLITERATOR_METHOD_INIT_VARS;
244 
245 	if (zend_parse_parameters_none() == FAILURE) {
246 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
247 			"IntlIterator::key: bad arguments", 0 TSRMLS_CC);
248 		return;
249 	}
250 
251 	INTLITERATOR_METHOD_FETCH_OBJECT;
252 
253 	if (ii->iterator->funcs->get_current_key) {
254 		ii->iterator->funcs->get_current_key(ii->iterator, return_value TSRMLS_CC);
255 	} else {
256 		RETURN_LONG(ii->iterator->index);
257 	}
258 }
259 
PHP_METHOD(IntlIterator,next)260 static PHP_METHOD(IntlIterator, next)
261 {
262 	INTLITERATOR_METHOD_INIT_VARS;
263 
264 	if (zend_parse_parameters_none() == FAILURE) {
265 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
266 			"IntlIterator::next: bad arguments", 0 TSRMLS_CC);
267 		return;
268 	}
269 
270 	INTLITERATOR_METHOD_FETCH_OBJECT;
271 	ii->iterator->funcs->move_forward(ii->iterator TSRMLS_CC);
272 	/* foreach also advances the index after the last iteration,
273 	 * so I see no problem in incrementing the index here unconditionally */
274 	ii->iterator->index++;
275 }
276 
PHP_METHOD(IntlIterator,rewind)277 static PHP_METHOD(IntlIterator, rewind)
278 {
279 	INTLITERATOR_METHOD_INIT_VARS;
280 
281 	if (zend_parse_parameters_none() == FAILURE) {
282 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
283 			"IntlIterator::rewind: bad arguments", 0 TSRMLS_CC);
284 		return;
285 	}
286 
287 	INTLITERATOR_METHOD_FETCH_OBJECT;
288 	if (ii->iterator->funcs->rewind) {
289 		ii->iterator->funcs->rewind(ii->iterator TSRMLS_CC);
290 	} else {
291 		intl_errors_set(INTLITERATOR_ERROR_P(ii), U_UNSUPPORTED_ERROR,
292 			"IntlIterator::rewind: rewind not supported", 0 TSRMLS_CC);
293 	}
294 }
295 
PHP_METHOD(IntlIterator,valid)296 static PHP_METHOD(IntlIterator, valid)
297 {
298 	INTLITERATOR_METHOD_INIT_VARS;
299 
300 	if (zend_parse_parameters_none() == FAILURE) {
301 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
302 			"IntlIterator::valid: bad arguments", 0 TSRMLS_CC);
303 		return;
304 	}
305 
306 	INTLITERATOR_METHOD_FETCH_OBJECT;
307 	RETURN_BOOL(ii->iterator->funcs->valid(ii->iterator TSRMLS_CC) == SUCCESS);
308 }
309 
310 ZEND_BEGIN_ARG_INFO_EX(ainfo_se_void, 0, 0, 0)
311 ZEND_END_ARG_INFO()
312 
313 static zend_function_entry IntlIterator_class_functions[] = {
314 	PHP_ME(IntlIterator,	current,	ainfo_se_void,			ZEND_ACC_PUBLIC)
315 	PHP_ME(IntlIterator,	key,		ainfo_se_void,			ZEND_ACC_PUBLIC)
316 	PHP_ME(IntlIterator,	next,		ainfo_se_void,			ZEND_ACC_PUBLIC)
317 	PHP_ME(IntlIterator,	rewind,		ainfo_se_void,			ZEND_ACC_PUBLIC)
318 	PHP_ME(IntlIterator,	valid,		ainfo_se_void,			ZEND_ACC_PUBLIC)
319 	PHP_FE_END
320 };
321 
322 
323 /* {{{ intl_register_IntlIterator_class
324  * Initialize 'IntlIterator' class
325  */
intl_register_IntlIterator_class(TSRMLS_D)326 U_CFUNC void intl_register_IntlIterator_class(TSRMLS_D)
327 {
328 	zend_class_entry ce;
329 
330 	/* Create and register 'IntlIterator' class. */
331 	INIT_CLASS_ENTRY(ce, "IntlIterator", IntlIterator_class_functions);
332 	ce.create_object = IntlIterator_object_create;
333 	IntlIterator_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
334 	IntlIterator_ce_ptr->get_iterator = IntlIterator_get_iterator;
335 	zend_class_implements(IntlIterator_ce_ptr TSRMLS_CC, 1,
336 		zend_ce_iterator);
337 
338 	memcpy(&IntlIterator_handlers, zend_get_std_object_handlers(),
339 		sizeof IntlIterator_handlers);
340 	IntlIterator_handlers.clone_obj = NULL;
341 
342 }
343