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 <unicode/brkiter.h>
22 
23 #include "breakiterator_iterators.h"
24 #include "../common/common_enum.h"
25 
26 extern "C" {
27 #define USE_BREAKITERATOR_POINTER
28 #include "breakiterator_class.h"
29 #include "../intl_convert.h"
30 #include "../locale/locale.h"
31 #include <zend_exceptions.h>
32 }
33 
34 static zend_class_entry *IntlPartsIterator_ce_ptr;
35 static zend_object_handlers IntlPartsIterator_handlers;
36 
37 /* BreakIterator's iterator */
38 
_breakiter_prolog(zend_object_iterator * iter TSRMLS_DC)39 inline BreakIterator *_breakiter_prolog(zend_object_iterator *iter TSRMLS_DC)
40 {
41 	BreakIterator_object *bio;
42 	bio = (BreakIterator_object*)zend_object_store_get_object(
43 			(const zval*)iter->data TSRMLS_CC);
44 	intl_errors_reset(BREAKITER_ERROR_P(bio) TSRMLS_CC);
45 	if (bio->biter == NULL) {
46 		intl_errors_set(BREAKITER_ERROR_P(bio), U_INVALID_STATE_ERROR,
47 			"The BreakIterator object backing the PHP iterator is not "
48 			"properly constructed", 0 TSRMLS_CC);
49 	}
50 	return bio->biter;
51 }
52 
_breakiterator_destroy_it(zend_object_iterator * iter TSRMLS_DC)53 static void _breakiterator_destroy_it(zend_object_iterator *iter TSRMLS_DC)
54 {
55 	zval_ptr_dtor((zval**)&iter->data);
56 }
57 
_breakiterator_move_forward(zend_object_iterator * iter TSRMLS_DC)58 static void _breakiterator_move_forward(zend_object_iterator *iter TSRMLS_DC)
59 {
60 	BreakIterator *biter = _breakiter_prolog(iter TSRMLS_CC);
61 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
62 
63 	iter->funcs->invalidate_current(iter TSRMLS_CC);
64 
65 	if (biter == NULL) {
66 		return;
67 	}
68 
69 	int32_t pos = biter->next();
70 	if (pos != BreakIterator::DONE) {
71 		MAKE_STD_ZVAL(zoi_iter->current);
72 		ZVAL_LONG(zoi_iter->current, (long)pos);
73 	} //else we've reached the end of the enum, nothing more is required
74 }
75 
_breakiterator_rewind(zend_object_iterator * iter TSRMLS_DC)76 static void _breakiterator_rewind(zend_object_iterator *iter TSRMLS_DC)
77 {
78 	BreakIterator *biter = _breakiter_prolog(iter TSRMLS_CC);
79 	zoi_with_current *zoi_iter = (zoi_with_current*)iter;
80 
81 	int32_t pos = biter->first();
82 	MAKE_STD_ZVAL(zoi_iter->current);
83 	ZVAL_LONG(zoi_iter->current, (long)pos);
84 }
85 
86 static zend_object_iterator_funcs breakiterator_iterator_funcs = {
87 	zoi_with_current_dtor,
88 	zoi_with_current_valid,
89 	zoi_with_current_get_current_data,
90 	NULL,
91 	_breakiterator_move_forward,
92 	_breakiterator_rewind,
93 	zoi_with_current_invalidate_current
94 };
95 
_breakiterator_get_iterator(zend_class_entry * ce,zval * object,int by_ref TSRMLS_DC)96 U_CFUNC zend_object_iterator *_breakiterator_get_iterator(
97 	zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
98 {
99 	BreakIterator_object *bio;
100 	if (by_ref) {
101 		zend_throw_exception(NULL,
102 			"Iteration by reference is not supported", 0 TSRMLS_CC);
103 		return NULL;
104 	}
105 
106 	bio = (BreakIterator_object*)zend_object_store_get_object(object TSRMLS_CC);
107 	BreakIterator *biter = bio->biter;
108 
109 	if (biter == NULL) {
110 		zend_throw_exception(NULL,
111 			"The BreakIterator is not properly constructed", 0 TSRMLS_CC);
112 		return NULL;
113 	}
114 
115 	zoi_with_current *zoi_iter =
116 		static_cast<zoi_with_current*>(emalloc(sizeof *zoi_iter));
117 	zoi_iter->zoi.data = static_cast<void*>(object);
118 	zoi_iter->zoi.funcs = &breakiterator_iterator_funcs;
119 	zoi_iter->zoi.index = 0;
120 	zoi_iter->destroy_it = _breakiterator_destroy_it;
121 	zoi_iter->wrapping_obj = NULL; /* not used; object is in zoi.data */
122 	zoi_iter->current = NULL;
123 
124 	zval_add_ref(&object);
125 
126 	return reinterpret_cast<zend_object_iterator *>(zoi_iter);
127 }
128 
129 /* BreakIterator parts iterator */
130 
131 typedef struct zoi_break_iter_parts {
132 	zoi_with_current zoi_cur;
133 	parts_iter_key_type key_type;
134 	BreakIterator_object *bio; /* so we don't have to fetch it all the time */
135 } zoi_break_iter_parts;
136 
_breakiterator_parts_destroy_it(zend_object_iterator * iter TSRMLS_DC)137 static void _breakiterator_parts_destroy_it(zend_object_iterator *iter TSRMLS_DC)
138 {
139 	zval_ptr_dtor(reinterpret_cast<zval**>(&iter->data));
140 }
141 
_breakiterator_parts_get_current_key(zend_object_iterator * iter,zval * key TSRMLS_DC)142 static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC)
143 {
144 	/* the actual work is done in move_forward and rewind */
145 	ZVAL_LONG(key, iter->index);
146 }
147 
_breakiterator_parts_move_forward(zend_object_iterator * iter TSRMLS_DC)148 static void _breakiterator_parts_move_forward(zend_object_iterator *iter TSRMLS_DC)
149 {
150 	zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
151 	BreakIterator_object *bio = zoi_bit->bio;
152 
153 	iter->funcs->invalidate_current(iter TSRMLS_CC);
154 
155 	int32_t cur,
156 			next;
157 
158 	cur = bio->biter->current();
159 	if (cur == BreakIterator::DONE) {
160 		return;
161 	}
162 	next = bio->biter->next();
163 	if (next == BreakIterator::DONE) {
164 		return;
165 	}
166 
167 	if (zoi_bit->key_type == PARTS_ITERATOR_KEY_LEFT) {
168 		iter->index = cur;
169 	} else if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT) {
170 		iter->index = next;
171 	}
172 	/* else zoi_bit->key_type == PARTS_ITERATOR_KEY_SEQUENTIAL
173 	 * No need to do anything, the engine increments ->index */
174 
175 	const char	*s = Z_STRVAL_P(bio->text);
176 	int32_t		slen = Z_STRLEN_P(bio->text),
177 				len;
178 	char		*res;
179 
180 	if (next == BreakIterator::DONE) {
181 		next = slen;
182 	}
183 	assert(next <= slen && next >= cur);
184 	len = next - cur;
185 	res = static_cast<char*>(emalloc(len + 1));
186 
187 	memcpy(res, &s[cur], len);
188 	res[len] = '\0';
189 
190 	MAKE_STD_ZVAL(zoi_bit->zoi_cur.current);
191 	ZVAL_STRINGL(zoi_bit->zoi_cur.current, res, len, 0);
192 }
193 
_breakiterator_parts_rewind(zend_object_iterator * iter TSRMLS_DC)194 static void _breakiterator_parts_rewind(zend_object_iterator *iter TSRMLS_DC)
195 {
196 	zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
197 	BreakIterator_object *bio = zoi_bit->bio;
198 
199 	if (zoi_bit->zoi_cur.current) {
200 		iter->funcs->invalidate_current(iter TSRMLS_CC);
201 	}
202 
203 	bio->biter->first();
204 
205 	iter->funcs->move_forward(iter TSRMLS_CC);
206 }
207 
208 static zend_object_iterator_funcs breakiterator_parts_it_funcs = {
209 	zoi_with_current_dtor,
210 	zoi_with_current_valid,
211 	zoi_with_current_get_current_data,
212 	_breakiterator_parts_get_current_key,
213 	_breakiterator_parts_move_forward,
214 	_breakiterator_parts_rewind,
215 	zoi_with_current_invalidate_current
216 };
217 
IntlIterator_from_BreakIterator_parts(zval * break_iter_zv,zval * object,parts_iter_key_type key_type TSRMLS_DC)218 void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,
219 										   zval *object,
220 										   parts_iter_key_type key_type TSRMLS_DC)
221 {
222 	IntlIterator_object *ii;
223 
224 	zval_add_ref(&break_iter_zv);
225 
226 	object_init_ex(object, IntlPartsIterator_ce_ptr);
227 	ii = (IntlIterator_object*)zend_object_store_get_object(object TSRMLS_CC);
228 
229 	ii->iterator = (zend_object_iterator*)emalloc(sizeof(zoi_break_iter_parts));
230 	ii->iterator->data = break_iter_zv;
231 	ii->iterator->funcs = &breakiterator_parts_it_funcs;
232 	ii->iterator->index = 0;
233 	((zoi_with_current*)ii->iterator)->destroy_it = _breakiterator_parts_destroy_it;
234 	((zoi_with_current*)ii->iterator)->wrapping_obj = object;
235 	((zoi_with_current*)ii->iterator)->current = NULL;
236 
237 	((zoi_break_iter_parts*)ii->iterator)->bio = (BreakIterator_object*)
238 		zend_object_store_get_object(break_iter_zv TSRMLS_CC);
239 	assert(((zoi_break_iter_parts*)ii->iterator)->bio->biter != NULL);
240 	((zoi_break_iter_parts*)ii->iterator)->key_type = key_type;
241 }
242 
IntlPartsIterator_object_create(zend_class_entry * ce TSRMLS_DC)243 U_CFUNC zend_object_value IntlPartsIterator_object_create(zend_class_entry *ce TSRMLS_DC)
244 {
245 	zend_object_value		retval;
246 
247 	retval = IntlIterator_ce_ptr->create_object(ce TSRMLS_CC);
248 	retval.handlers = &IntlPartsIterator_handlers;
249 
250 	return retval;
251 }
252 
IntlPartsIterator_get_method(zval ** object_ptr,char * method,int method_len,const zend_literal * key TSRMLS_DC)253 U_CFUNC zend_function *IntlPartsIterator_get_method(zval **object_ptr,
254 		char *method, int method_len, const zend_literal *key TSRMLS_DC)
255 {
256 	zend_literal local_literal = {0};
257 	zend_function *ret;
258 	ALLOCA_FLAG(use_heap)
259 
260 	if (key == NULL) {
261 		Z_STRVAL(local_literal.constant) = static_cast<char*>(
262 				do_alloca(method_len + 1, use_heap));
263 		zend_str_tolower_copy(Z_STRVAL(local_literal.constant),
264 				method, method_len);
265 		local_literal.hash_value = zend_hash_func(
266 				Z_STRVAL(local_literal.constant), method_len + 1);
267 		key = &local_literal;
268 	}
269 
270 	if ((key->hash_value & 0xFFFFFFFF) == 0xA2B486A1 /* hash of getrulestatus\0 */
271 			&& method_len == sizeof("getrulestatus") - 1
272 			&& memcmp("getrulestatus", Z_STRVAL(key->constant),	method_len) == 0) {
273 		IntlIterator_object *obj = (IntlIterator_object*)
274 				zend_object_store_get_object(*object_ptr TSRMLS_CC);
275 		if (obj->iterator && obj->iterator->data) {
276 			zval *break_iter_zv = static_cast<zval*>(obj->iterator->data);
277 			*object_ptr = break_iter_zv;
278 			ret = Z_OBJ_HANDLER_P(break_iter_zv, get_method)(object_ptr,
279 					method, method_len, key TSRMLS_CC);
280 			goto end;
281 		}
282 	}
283 
284 	ret = std_object_handlers.get_method(object_ptr,
285 			method, method_len, key TSRMLS_CC);
286 
287 end:
288 	if (key == &local_literal) {
289 		free_alloca(Z_STRVAL(local_literal.constant), use_heap);
290 	}
291 
292 	return ret;
293 }
294 
PHP_METHOD(IntlPartsIterator,getBreakIterator)295 U_CFUNC PHP_METHOD(IntlPartsIterator, getBreakIterator)
296 {
297 	INTLITERATOR_METHOD_INIT_VARS;
298 
299 	if (zend_parse_parameters_none() == FAILURE) {
300 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
301 			"IntlPartsIterator::getBreakIterator: bad arguments", 0 TSRMLS_CC);
302 		return;
303 	}
304 
305 	INTLITERATOR_METHOD_FETCH_OBJECT;
306 
307 	zval *biter_zval = static_cast<zval*>(ii->iterator->data);
308 	RETURN_ZVAL(biter_zval, 1, 0);
309 }
310 
311 ZEND_BEGIN_ARG_INFO_EX(ainfo_parts_it_void, 0, 0, 0)
312 ZEND_END_ARG_INFO()
313 
314 static const zend_function_entry IntlPartsIterator_class_functions[] = {
315 	PHP_ME(IntlPartsIterator,	getBreakIterator,	ainfo_parts_it_void,	ZEND_ACC_PUBLIC)
316 	PHP_FE_END
317 };
318 
breakiterator_register_IntlPartsIterator_class(TSRMLS_D)319 U_CFUNC void breakiterator_register_IntlPartsIterator_class(TSRMLS_D)
320 {
321 	zend_class_entry ce;
322 
323 	/* Create and register 'BreakIterator' class. */
324 	INIT_CLASS_ENTRY(ce, "IntlPartsIterator", IntlPartsIterator_class_functions);
325 	IntlPartsIterator_ce_ptr = zend_register_internal_class_ex(&ce,
326 			IntlIterator_ce_ptr, NULL TSRMLS_CC);
327 	IntlPartsIterator_ce_ptr->create_object = IntlPartsIterator_object_create;
328 
329 	memcpy(&IntlPartsIterator_handlers, &IntlIterator_handlers,
330 			sizeof IntlPartsIterator_handlers);
331 	IntlPartsIterator_handlers.get_method = IntlPartsIterator_get_method;
332 
333 #define PARTSITER_DECL_LONG_CONST(name) \
334 	zend_declare_class_constant_long(IntlPartsIterator_ce_ptr, #name, \
335 		sizeof(#name) - 1, PARTS_ITERATOR_ ## name TSRMLS_CC)
336 
337 	PARTSITER_DECL_LONG_CONST(KEY_SEQUENTIAL);
338 	PARTSITER_DECL_LONG_CONST(KEY_LEFT);
339 	PARTSITER_DECL_LONG_CONST(KEY_RIGHT);
340 
341 #undef PARTSITER_DECL_LONG_CONST
342 }
343