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