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#include "codepointiterator_internal.h"
23
24#include "breakiterator_iterators.h"
25
26extern "C" {
27#include "../php_intl.h"
28#define USE_BREAKITERATOR_POINTER 1
29#include "breakiterator_class.h"
30#include "../locale/locale.h"
31#include <zend_exceptions.h>
32}
33
34using PHP::CodePointBreakIterator;
35
36U_CFUNC PHP_METHOD(BreakIterator, __construct)
37{
38    zend_throw_exception( NULL,
39        "An object of this type cannot be created with the new operator",
40        0 );
41}
42
43static void _breakiter_factory(const char *func_name,
44                               BreakIterator *(*func)(const Locale&, UErrorCode&),
45                               INTERNAL_FUNCTION_PARAMETERS)
46{
47    BreakIterator   *biter;
48    const char      *locale_str = NULL;
49    size_t              dummy;
50    char            *msg;
51    UErrorCode      status = UErrorCode();
52    intl_error_reset(NULL);
53
54    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!",
55            &locale_str, &dummy) == FAILURE) {
56        spprintf(&msg, 0, "%s: bad arguments", func_name);
57        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
58        efree(msg);
59        RETURN_NULL();
60    }
61
62    if (locale_str == NULL) {
63        locale_str = intl_locale_get_default();
64    }
65
66    biter = func(Locale::createFromName(locale_str), status);
67    intl_error_set_code(NULL, status);
68    if (U_FAILURE(status)) {
69        spprintf(&msg, 0, "%s: error creating BreakIterator",
70                func_name);
71        intl_error_set_custom_msg(NULL, msg, 1);
72        efree(msg);
73        RETURN_NULL();
74    }
75
76    breakiterator_object_create(return_value, biter, 1);
77}
78
79U_CFUNC PHP_FUNCTION(breakiter_create_word_instance)
80{
81    _breakiter_factory("breakiter_create_word_instance",
82            &BreakIterator::createWordInstance,
83            INTERNAL_FUNCTION_PARAM_PASSTHRU);
84}
85
86U_CFUNC PHP_FUNCTION(breakiter_create_line_instance)
87{
88    _breakiter_factory("breakiter_create_line_instance",
89            &BreakIterator::createLineInstance,
90            INTERNAL_FUNCTION_PARAM_PASSTHRU);
91}
92
93U_CFUNC PHP_FUNCTION(breakiter_create_character_instance)
94{
95    _breakiter_factory("breakiter_create_character_instance",
96            &BreakIterator::createCharacterInstance,
97            INTERNAL_FUNCTION_PARAM_PASSTHRU);
98}
99
100U_CFUNC PHP_FUNCTION(breakiter_create_sentence_instance)
101{
102    _breakiter_factory("breakiter_create_sentence_instance",
103            &BreakIterator::createSentenceInstance,
104            INTERNAL_FUNCTION_PARAM_PASSTHRU);
105}
106
107U_CFUNC PHP_FUNCTION(breakiter_create_title_instance)
108{
109    _breakiter_factory("breakiter_create_title_instance",
110            &BreakIterator::createTitleInstance,
111            INTERNAL_FUNCTION_PARAM_PASSTHRU);
112}
113
114U_CFUNC PHP_FUNCTION(breakiter_create_code_point_instance)
115{
116    UErrorCode status = UErrorCode();
117    intl_error_reset(NULL);
118
119    if (zend_parse_parameters_none() == FAILURE) {
120        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
121            "breakiter_create_code_point_instance: bad arguments", 0);
122        RETURN_NULL();
123    }
124
125    CodePointBreakIterator *cpbi = new CodePointBreakIterator();
126    breakiterator_object_create(return_value, cpbi, 1);
127}
128
129U_CFUNC PHP_FUNCTION(breakiter_get_text)
130{
131    BREAKITER_METHOD_INIT_VARS;
132    object = getThis();
133
134    if (zend_parse_parameters_none() == FAILURE) {
135        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
136            "breakiter_get_text: bad arguments", 0);
137        RETURN_FALSE;
138    }
139
140    BREAKITER_METHOD_FETCH_OBJECT;
141
142    if (Z_ISUNDEF(bio->text)) {
143        RETURN_NULL();
144    } else {
145        ZVAL_COPY(return_value, &bio->text);
146    }
147}
148
149U_CFUNC PHP_FUNCTION(breakiter_set_text)
150{
151    UText   *ut = NULL;
152    zend_string *text;
153    BREAKITER_METHOD_INIT_VARS;
154    object = getThis();
155
156    if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &text) == FAILURE) {
157        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
158            "breakiter_set_text: bad arguments", 0);
159        RETURN_FALSE;
160    }
161
162    BREAKITER_METHOD_FETCH_OBJECT;
163
164    ut = utext_openUTF8(ut, ZSTR_VAL(text), ZSTR_LEN(text), BREAKITER_ERROR_CODE_P(bio));
165    INTL_METHOD_CHECK_STATUS_OR_NULL(bio, "breakiter_set_text: error opening UText");
166
167    bio->biter->setText(ut, BREAKITER_ERROR_CODE(bio));
168    utext_close(ut); /* ICU shallow clones the UText */
169    INTL_METHOD_CHECK_STATUS_OR_NULL(bio, "breakiter_set_text: error calling "
170        "BreakIterator::setText()");
171
172    /* When ICU clones the UText, it does not copy the buffer, so we have to
173     * keep the string buffer around by holding a reference to its zval. This
174     * also allows a faste implementation of getText() */
175    zval_ptr_dtor(&bio->text);
176    ZVAL_STR_COPY(&bio->text, text);
177
178    RETURN_TRUE;
179}
180
181static void _breakiter_no_args_ret_int32(
182        const char *func_name,
183        int32_t (BreakIterator::*func)(),
184        INTERNAL_FUNCTION_PARAMETERS)
185{
186    char    *msg;
187    BREAKITER_METHOD_INIT_VARS;
188    object = getThis();
189
190    if (zend_parse_parameters_none() == FAILURE) {
191        spprintf(&msg, 0, "%s: bad arguments", func_name);
192        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
193        efree(msg);
194        RETURN_FALSE;
195    }
196
197    BREAKITER_METHOD_FETCH_OBJECT;
198
199    int32_t res = (bio->biter->*func)();
200
201    RETURN_LONG((zend_long)res);
202}
203
204static void _breakiter_int32_ret_int32(
205        const char *func_name,
206        int32_t (BreakIterator::*func)(int32_t),
207        INTERNAL_FUNCTION_PARAMETERS)
208{
209    char    *msg;
210    zend_long   arg;
211    BREAKITER_METHOD_INIT_VARS;
212    object = getThis();
213
214    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &arg) == FAILURE) {
215        spprintf(&msg, 0, "%s: bad arguments", func_name);
216        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
217        efree(msg);
218        RETURN_FALSE;
219    }
220
221    BREAKITER_METHOD_FETCH_OBJECT;
222
223    if (arg < INT32_MIN || arg > INT32_MAX) {
224        spprintf(&msg, 0, "%s: offset argument is outside bounds of "
225                "a 32-bit wide integer", func_name);
226        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
227        efree(msg);
228        RETURN_FALSE;
229    }
230
231    int32_t res = (bio->biter->*func)((int32_t)arg);
232
233    RETURN_LONG((zend_long)res);
234}
235
236U_CFUNC PHP_FUNCTION(breakiter_first)
237{
238    _breakiter_no_args_ret_int32("breakiter_first",
239            &BreakIterator::first,
240            INTERNAL_FUNCTION_PARAM_PASSTHRU);
241}
242
243U_CFUNC PHP_FUNCTION(breakiter_last)
244{
245    _breakiter_no_args_ret_int32("breakiter_last",
246            &BreakIterator::last,
247            INTERNAL_FUNCTION_PARAM_PASSTHRU);
248}
249
250U_CFUNC PHP_FUNCTION(breakiter_previous)
251{
252    _breakiter_no_args_ret_int32("breakiter_previous",
253            &BreakIterator::previous,
254            INTERNAL_FUNCTION_PARAM_PASSTHRU);
255}
256
257U_CFUNC PHP_FUNCTION(breakiter_next)
258{
259    bool no_arg_version = false;
260
261    if (ZEND_NUM_ARGS() == 0) {
262        no_arg_version = true;
263    } else if (ZEND_NUM_ARGS() == 1) {
264        zval *arg;
265        int res = zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg);
266        assert(res == SUCCESS);
267        if (Z_TYPE_P(arg) == IS_NULL) {
268            no_arg_version = true;
269            ZEND_NUM_ARGS() = 0; /* pretend we don't have any argument */
270        } else {
271            no_arg_version = false;
272        }
273    }
274
275    if (no_arg_version) {
276        _breakiter_no_args_ret_int32("breakiter_next",
277                &BreakIterator::next,
278                INTERNAL_FUNCTION_PARAM_PASSTHRU);
279    } else {
280        _breakiter_int32_ret_int32("breakiter_next",
281                &BreakIterator::next,
282                INTERNAL_FUNCTION_PARAM_PASSTHRU);
283    }
284}
285
286U_CFUNC PHP_FUNCTION(breakiter_current)
287{
288    BREAKITER_METHOD_INIT_VARS;
289    object = getThis();
290
291    if (zend_parse_parameters_none() == FAILURE) {
292        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
293                "breakiter_current: bad arguments", 0);
294        RETURN_FALSE;
295    }
296
297    BREAKITER_METHOD_FETCH_OBJECT;
298
299    int32_t res = bio->biter->current();
300
301    RETURN_LONG((zend_long)res);
302}
303
304U_CFUNC PHP_FUNCTION(breakiter_following)
305{
306    _breakiter_int32_ret_int32("breakiter_following",
307            &BreakIterator::following,
308            INTERNAL_FUNCTION_PARAM_PASSTHRU);
309}
310
311U_CFUNC PHP_FUNCTION(breakiter_preceding)
312{
313    _breakiter_int32_ret_int32("breakiter_preceding",
314            &BreakIterator::preceding,
315            INTERNAL_FUNCTION_PARAM_PASSTHRU);
316}
317
318U_CFUNC PHP_FUNCTION(breakiter_is_boundary)
319{
320    zend_long offset;
321    BREAKITER_METHOD_INIT_VARS;
322    object = getThis();
323
324    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l",
325            &offset) == FAILURE) {
326        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
327                "breakiter_is_boundary: bad arguments", 0);
328        RETURN_FALSE;
329    }
330
331    if (offset < INT32_MIN || offset > INT32_MAX) {
332        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
333                "breakiter_is_boundary: offset argument is outside bounds of "
334                "a 32-bit wide integer", 0);
335        RETURN_FALSE;
336    }
337
338    BREAKITER_METHOD_FETCH_OBJECT;
339
340    UBool res = bio->biter->isBoundary((int32_t)offset);
341
342    RETURN_BOOL((zend_long)res);
343}
344
345U_CFUNC PHP_FUNCTION(breakiter_get_locale)
346{
347    zend_long   locale_type;
348    BREAKITER_METHOD_INIT_VARS;
349    object = getThis();
350
351    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &locale_type) == FAILURE) {
352        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
353            "breakiter_get_locale: bad arguments", 0);
354        RETURN_FALSE;
355    }
356
357    if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
358        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
359            "breakiter_get_locale: invalid locale type", 0);
360        RETURN_FALSE;
361    }
362
363    BREAKITER_METHOD_FETCH_OBJECT;
364
365    Locale locale = bio->biter->getLocale((ULocDataLocaleType)locale_type,
366        BREAKITER_ERROR_CODE(bio));
367    INTL_METHOD_CHECK_STATUS(bio,
368        "breakiter_get_locale: Call to ICU method has failed");
369
370    RETURN_STRING(locale.getName());
371}
372
373U_CFUNC PHP_FUNCTION(breakiter_get_parts_iterator)
374{
375    zend_long key_type = 0;
376    BREAKITER_METHOD_INIT_VARS;
377    object = getThis();
378
379    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &key_type) == FAILURE) {
380        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
381            "breakiter_get_parts_iterator: bad arguments", 0);
382        RETURN_FALSE;
383    }
384
385    if (key_type != PARTS_ITERATOR_KEY_SEQUENTIAL
386            && key_type != PARTS_ITERATOR_KEY_LEFT
387            && key_type != PARTS_ITERATOR_KEY_RIGHT) {
388        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
389            "breakiter_get_parts_iterator: bad key type", 0);
390        RETURN_FALSE;
391    }
392
393    BREAKITER_METHOD_FETCH_OBJECT;
394
395    IntlIterator_from_BreakIterator_parts(
396        object, return_value, (parts_iter_key_type)key_type);
397}
398
399U_CFUNC PHP_FUNCTION(breakiter_get_error_code)
400{
401    BREAKITER_METHOD_INIT_VARS;
402    object = getThis();
403
404    if (zend_parse_parameters_none() == FAILURE) {
405        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
406            "breakiter_get_error_code: bad arguments", 0);
407        RETURN_FALSE;
408    }
409
410    /* Fetch the object (without resetting its last error code ). */
411    bio = Z_INTL_BREAKITERATOR_P(object);
412    if (bio == NULL)
413        RETURN_FALSE;
414
415    RETURN_LONG((zend_long)BREAKITER_ERROR_CODE(bio));
416}
417
418U_CFUNC PHP_FUNCTION(breakiter_get_error_message)
419{
420    zend_string* message = NULL;
421    BREAKITER_METHOD_INIT_VARS;
422    object = getThis();
423
424    if (zend_parse_parameters_none() == FAILURE) {
425        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
426            "breakiter_get_error_message: bad arguments", 0 );
427        RETURN_FALSE;
428    }
429
430
431    /* Fetch the object (without resetting its last error code ). */
432    bio = Z_INTL_BREAKITERATOR_P(object);
433    if (bio == NULL)
434        RETURN_FALSE;
435
436    /* Return last error message. */
437    message = intl_error_get_message(BREAKITER_ERROR_P(bio));
438    RETURN_STR(message);
439}
440