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