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 const 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 Z_ADDREF_P(object);
115 ZVAL_OBJ(&zoi_iter->zoi.data, Z_OBJ_P(object));
116 zoi_iter->zoi.funcs = &breakiterator_iterator_funcs;
117 zoi_iter->zoi.index = 0;
118 zoi_iter->destroy_it = _breakiterator_destroy_it;
119 ZVAL_UNDEF(&zoi_iter->wrapping_obj); /* not used; object is in zoi.data */
120 ZVAL_UNDEF(&zoi_iter->current);
121
122 return reinterpret_cast<zend_object_iterator *>(zoi_iter);
123 }
124
125 /* BreakIterator parts iterator */
126
127 typedef struct zoi_break_iter_parts {
128 zoi_with_current zoi_cur;
129 parts_iter_key_type key_type;
130 BreakIterator_object *bio; /* so we don't have to fetch it all the time */
131 } zoi_break_iter_parts;
132
_breakiterator_parts_destroy_it(zend_object_iterator * iter)133 static void _breakiterator_parts_destroy_it(zend_object_iterator *iter)
134 {
135 zval_ptr_dtor(&iter->data);
136 }
137
_breakiterator_parts_get_current_key(zend_object_iterator * iter,zval * key)138 static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key)
139 {
140 /* the actual work is done in move_forward and rewind */
141 ZVAL_LONG(key, iter->index);
142 }
143
_breakiterator_parts_move_forward(zend_object_iterator * iter)144 static void _breakiterator_parts_move_forward(zend_object_iterator *iter)
145 {
146 zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
147 BreakIterator_object *bio = zoi_bit->bio;
148
149 iter->funcs->invalidate_current(iter);
150
151 int32_t cur,
152 next;
153
154 cur = bio->biter->current();
155 if (cur == BreakIterator::DONE) {
156 return;
157 }
158 next = bio->biter->next();
159 if (next == BreakIterator::DONE) {
160 return;
161 }
162
163 if (zoi_bit->key_type == PARTS_ITERATOR_KEY_LEFT) {
164 iter->index = cur;
165 } else if (zoi_bit->key_type == PARTS_ITERATOR_KEY_RIGHT) {
166 iter->index = next;
167 }
168 /* else zoi_bit->key_type == PARTS_ITERATOR_KEY_SEQUENTIAL
169 * No need to do anything, the engine increments ->index */
170
171 const char *s = Z_STRVAL(bio->text);
172 zend_string *res;
173
174 assert(next <= Z_STRLEN(bio->text) && next >= cur);
175 res = zend_string_alloc(next - cur, 0);
176
177 memcpy(ZSTR_VAL(res), &s[cur], ZSTR_LEN(res));
178 ZSTR_VAL(res)[ZSTR_LEN(res)] = '\0';
179
180 ZVAL_STR(&zoi_bit->zoi_cur.current, res);
181 }
182
_breakiterator_parts_rewind(zend_object_iterator * iter)183 static void _breakiterator_parts_rewind(zend_object_iterator *iter)
184 {
185 zoi_break_iter_parts *zoi_bit = (zoi_break_iter_parts*)iter;
186 BreakIterator_object *bio = zoi_bit->bio;
187
188 if (!Z_ISUNDEF(zoi_bit->zoi_cur.current)) {
189 iter->funcs->invalidate_current(iter);
190 }
191
192 bio->biter->first();
193
194 iter->funcs->move_forward(iter);
195 }
196
197 static const zend_object_iterator_funcs breakiterator_parts_it_funcs = {
198 zoi_with_current_dtor,
199 zoi_with_current_valid,
200 zoi_with_current_get_current_data,
201 _breakiterator_parts_get_current_key,
202 _breakiterator_parts_move_forward,
203 _breakiterator_parts_rewind,
204 zoi_with_current_invalidate_current
205 };
206
IntlIterator_from_BreakIterator_parts(zval * break_iter_zv,zval * object,parts_iter_key_type key_type)207 void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,
208 zval *object,
209 parts_iter_key_type key_type)
210 {
211 IntlIterator_object *ii;
212
213 object_init_ex(object, IntlPartsIterator_ce_ptr);
214 ii = Z_INTL_ITERATOR_P(object);
215
216 ii->iterator = (zend_object_iterator*)emalloc(sizeof(zoi_break_iter_parts));
217 zend_iterator_init(ii->iterator);
218
219 ZVAL_COPY(&ii->iterator->data, break_iter_zv);
220 ii->iterator->funcs = &breakiterator_parts_it_funcs;
221 ii->iterator->index = 0;
222
223 ((zoi_with_current*)ii->iterator)->destroy_it = _breakiterator_parts_destroy_it;
224 ZVAL_OBJ(&((zoi_with_current*)ii->iterator)->wrapping_obj, Z_OBJ_P(object));
225 ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->current);
226
227 ((zoi_break_iter_parts*)ii->iterator)->bio = Z_INTL_BREAKITERATOR_P(break_iter_zv);
228
229 assert(((zoi_break_iter_parts*)ii->iterator)->bio->biter != NULL);
230
231 ((zoi_break_iter_parts*)ii->iterator)->key_type = key_type;
232 }
233
IntlPartsIterator_object_create(zend_class_entry * ce)234 U_CFUNC zend_object *IntlPartsIterator_object_create(zend_class_entry *ce)
235 {
236 zend_object *retval = IntlIterator_ce_ptr->create_object(ce);
237 retval->handlers = &IntlPartsIterator_handlers;
238
239 return retval;
240 }
241
IntlPartsIterator_get_method(zend_object ** object_ptr,zend_string * method,const zval * key)242 U_CFUNC zend_function *IntlPartsIterator_get_method(zend_object **object_ptr, zend_string *method, const zval *key)
243 {
244 zend_function *ret;
245 zend_string *lc_method_name;
246 ALLOCA_FLAG(use_heap);
247
248 if (key == NULL) {
249 ZSTR_ALLOCA_ALLOC(lc_method_name, ZSTR_LEN(method), use_heap);
250 zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method), ZSTR_LEN(method));
251 } else {
252 lc_method_name = Z_STR_P(key);
253 }
254
255 if (ZSTR_LEN(method) == sizeof("getrulestatus") - 1
256 && memcmp("getrulestatus", ZSTR_VAL(lc_method_name), ZSTR_LEN(lc_method_name)) == 0) {
257 IntlIterator_object *obj = php_intl_iterator_fetch_object(*object_ptr);
258 if (obj->iterator && !Z_ISUNDEF(obj->iterator->data)) {
259 zval *break_iter_zv = &obj->iterator->data;
260 *object_ptr = Z_OBJ_P(break_iter_zv);
261 ret = Z_OBJ_HANDLER_P(break_iter_zv, get_method)(object_ptr, method, key);
262 goto end;
263 }
264 }
265
266 ret = zend_std_get_method(object_ptr, method, key);
267
268 end:
269 if (key == NULL) {
270 ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
271 }
272
273 return ret;
274 }
275
PHP_METHOD(IntlPartsIterator,getBreakIterator)276 U_CFUNC PHP_METHOD(IntlPartsIterator, getBreakIterator)
277 {
278 INTLITERATOR_METHOD_INIT_VARS;
279
280 if (zend_parse_parameters_none() == FAILURE) {
281 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
282 "IntlPartsIterator::getBreakIterator: bad arguments", 0);
283 return;
284 }
285
286 INTLITERATOR_METHOD_FETCH_OBJECT;
287
288 zval *biter_zval = &ii->iterator->data;
289 ZVAL_COPY_DEREF(return_value, biter_zval);
290 }
291
292 ZEND_BEGIN_ARG_INFO_EX(ainfo_parts_it_void, 0, 0, 0)
293 ZEND_END_ARG_INFO()
294
295 static const zend_function_entry IntlPartsIterator_class_functions[] = {
296 PHP_ME(IntlPartsIterator, getBreakIterator, ainfo_parts_it_void, ZEND_ACC_PUBLIC)
297 PHP_FE_END
298 };
299
breakiterator_register_IntlPartsIterator_class(void)300 U_CFUNC void breakiterator_register_IntlPartsIterator_class(void)
301 {
302 zend_class_entry ce;
303
304 /* Create and register 'BreakIterator' class. */
305 INIT_CLASS_ENTRY(ce, "IntlPartsIterator", IntlPartsIterator_class_functions);
306 IntlPartsIterator_ce_ptr = zend_register_internal_class_ex(&ce,
307 IntlIterator_ce_ptr);
308 IntlPartsIterator_ce_ptr->create_object = IntlPartsIterator_object_create;
309
310 memcpy(&IntlPartsIterator_handlers, &IntlIterator_handlers,
311 sizeof IntlPartsIterator_handlers);
312 IntlPartsIterator_handlers.get_method = IntlPartsIterator_get_method;
313
314 #define PARTSITER_DECL_LONG_CONST(name) \
315 zend_declare_class_constant_long(IntlPartsIterator_ce_ptr, #name, \
316 sizeof(#name) - 1, PARTS_ITERATOR_ ## name)
317
318 PARTSITER_DECL_LONG_CONST(KEY_SEQUENTIAL);
319 PARTSITER_DECL_LONG_CONST(KEY_LEFT);
320 PARTSITER_DECL_LONG_CONST(KEY_RIGHT);
321
322 #undef PARTSITER_DECL_LONG_CONST
323 }
324