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