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    char    *text;
152    size_t      text_len;
153    UText   *ut = NULL;
154    zval    *textzv;
155    BREAKITER_METHOD_INIT_VARS;
156    object = getThis();
157
158    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
159            &text, &text_len) == FAILURE) {
160        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
161            "breakiter_set_text: bad arguments", 0);
162        RETURN_FALSE;
163    }
164
165    int res = zend_get_parameters_ex(1, &textzv);
166    assert(res == SUCCESS);
167
168    BREAKITER_METHOD_FETCH_OBJECT;
169
170    /* assert it's safe to use text and text_len because zpp changes the
171     * arguments in the stack */
172    assert(text == Z_STRVAL_P(textzv));
173
174    ut = utext_openUTF8(ut, text, text_len, BREAKITER_ERROR_CODE_P(bio));
175    INTL_CTOR_CHECK_STATUS(bio, "breakiter_set_text: error opening UText");
176
177    bio->biter->setText(ut, BREAKITER_ERROR_CODE(bio));
178    utext_close(ut); /* ICU shallow clones the UText */
179    INTL_CTOR_CHECK_STATUS(bio, "breakiter_set_text: error calling "
180        "BreakIterator::setText()");
181
182    /* When ICU clones the UText, it does not copy the buffer, so we have to
183     * keep the string buffer around by holding a reference to its zval. This
184     * also allows a faste implementation of getText() */
185    zval_ptr_dtor(&bio->text);
186    ZVAL_COPY(&bio->text, textzv);
187
188    RETURN_TRUE;
189}
190
191static void _breakiter_no_args_ret_int32(
192        const char *func_name,
193        int32_t (BreakIterator::*func)(),
194        INTERNAL_FUNCTION_PARAMETERS)
195{
196    char    *msg;
197    BREAKITER_METHOD_INIT_VARS;
198    object = getThis();
199
200    if (zend_parse_parameters_none() == FAILURE) {
201        spprintf(&msg, 0, "%s: bad arguments", func_name);
202        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
203        efree(msg);
204        RETURN_FALSE;
205    }
206
207    BREAKITER_METHOD_FETCH_OBJECT;
208
209    int32_t res = (bio->biter->*func)();
210
211    RETURN_LONG((zend_long)res);
212}
213
214static void _breakiter_int32_ret_int32(
215        const char *func_name,
216        int32_t (BreakIterator::*func)(int32_t),
217        INTERNAL_FUNCTION_PARAMETERS)
218{
219    char    *msg;
220    zend_long   arg;
221    BREAKITER_METHOD_INIT_VARS;
222    object = getThis();
223
224    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &arg) == FAILURE) {
225        spprintf(&msg, 0, "%s: bad arguments", func_name);
226        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
227        efree(msg);
228        RETURN_FALSE;
229    }
230
231    BREAKITER_METHOD_FETCH_OBJECT;
232
233    if (arg < INT32_MIN || arg > INT32_MAX) {
234        spprintf(&msg, 0, "%s: offset argument is outside bounds of "
235                "a 32-bit wide integer", func_name);
236        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1);
237        efree(msg);
238        RETURN_FALSE;
239    }
240
241    int32_t res = (bio->biter->*func)((int32_t)arg);
242
243    RETURN_LONG((zend_long)res);
244}
245
246U_CFUNC PHP_FUNCTION(breakiter_first)
247{
248    _breakiter_no_args_ret_int32("breakiter_first",
249            &BreakIterator::first,
250            INTERNAL_FUNCTION_PARAM_PASSTHRU);
251}
252
253U_CFUNC PHP_FUNCTION(breakiter_last)
254{
255    _breakiter_no_args_ret_int32("breakiter_last",
256            &BreakIterator::last,
257            INTERNAL_FUNCTION_PARAM_PASSTHRU);
258}
259
260U_CFUNC PHP_FUNCTION(breakiter_previous)
261{
262    _breakiter_no_args_ret_int32("breakiter_previous",
263            &BreakIterator::previous,
264            INTERNAL_FUNCTION_PARAM_PASSTHRU);
265}
266
267U_CFUNC PHP_FUNCTION(breakiter_next)
268{
269    bool no_arg_version = false;
270
271    if (ZEND_NUM_ARGS() == 0) {
272        no_arg_version = true;
273    } else if (ZEND_NUM_ARGS() == 1) {
274        zval *arg;
275        int res = zend_get_parameters_ex(1, &arg);
276        assert(res == SUCCESS);
277        if (Z_TYPE_P(arg) == IS_NULL) {
278            no_arg_version = true;
279            ZEND_NUM_ARGS() = 0; /* pretend we don't have any argument */
280        } else {
281            no_arg_version = false;
282        }
283    }
284
285    if (no_arg_version) {
286        _breakiter_no_args_ret_int32("breakiter_next",
287                &BreakIterator::next,
288                INTERNAL_FUNCTION_PARAM_PASSTHRU);
289    } else {
290        _breakiter_int32_ret_int32("breakiter_next",
291                &BreakIterator::next,
292                INTERNAL_FUNCTION_PARAM_PASSTHRU);
293    }
294}
295
296U_CFUNC PHP_FUNCTION(breakiter_current)
297{
298    BREAKITER_METHOD_INIT_VARS;
299    object = getThis();
300
301    if (zend_parse_parameters_none() == FAILURE) {
302        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
303                "breakiter_current: bad arguments", 0);
304        RETURN_FALSE;
305    }
306
307    BREAKITER_METHOD_FETCH_OBJECT;
308
309    int32_t res = bio->biter->current();
310
311    RETURN_LONG((zend_long)res);
312}
313
314U_CFUNC PHP_FUNCTION(breakiter_following)
315{
316    _breakiter_int32_ret_int32("breakiter_following",
317            &BreakIterator::following,
318            INTERNAL_FUNCTION_PARAM_PASSTHRU);
319}
320
321U_CFUNC PHP_FUNCTION(breakiter_preceding)
322{
323    _breakiter_int32_ret_int32("breakiter_preceding",
324            &BreakIterator::preceding,
325            INTERNAL_FUNCTION_PARAM_PASSTHRU);
326}
327
328U_CFUNC PHP_FUNCTION(breakiter_is_boundary)
329{
330    zend_long offset;
331    BREAKITER_METHOD_INIT_VARS;
332    object = getThis();
333
334    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l",
335            &offset) == FAILURE) {
336        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
337                "breakiter_is_boundary: bad arguments", 0);
338        RETURN_FALSE;
339    }
340
341    if (offset < INT32_MIN || offset > INT32_MAX) {
342        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
343                "breakiter_is_boundary: offset argument is outside bounds of "
344                "a 32-bit wide integer", 0);
345        RETURN_FALSE;
346    }
347
348    BREAKITER_METHOD_FETCH_OBJECT;
349
350    UBool res = bio->biter->isBoundary((int32_t)offset);
351
352    RETURN_BOOL((zend_long)res);
353}
354
355U_CFUNC PHP_FUNCTION(breakiter_get_locale)
356{
357    zend_long   locale_type;
358    BREAKITER_METHOD_INIT_VARS;
359    object = getThis();
360
361    if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &locale_type) == FAILURE) {
362        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
363            "breakiter_get_locale: bad arguments", 0);
364        RETURN_FALSE;
365    }
366
367    if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
368        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
369            "breakiter_get_locale: invalid locale type", 0);
370        RETURN_FALSE;
371    }
372
373    BREAKITER_METHOD_FETCH_OBJECT;
374
375    Locale locale = bio->biter->getLocale((ULocDataLocaleType)locale_type,
376        BREAKITER_ERROR_CODE(bio));
377    INTL_METHOD_CHECK_STATUS(bio,
378        "breakiter_get_locale: Call to ICU method has failed");
379
380    RETURN_STRING(locale.getName());
381}
382
383U_CFUNC PHP_FUNCTION(breakiter_get_parts_iterator)
384{
385    zend_long key_type = 0;
386    BREAKITER_METHOD_INIT_VARS;
387    object = getThis();
388
389    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &key_type) == FAILURE) {
390        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
391            "breakiter_get_parts_iterator: bad arguments", 0);
392        RETURN_FALSE;
393    }
394
395    if (key_type != PARTS_ITERATOR_KEY_SEQUENTIAL
396            && key_type != PARTS_ITERATOR_KEY_LEFT
397            && key_type != PARTS_ITERATOR_KEY_RIGHT) {
398        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
399            "breakiter_get_parts_iterator: bad key type", 0);
400        RETURN_FALSE;
401    }
402
403    BREAKITER_METHOD_FETCH_OBJECT;
404
405    IntlIterator_from_BreakIterator_parts(
406        object, return_value, (parts_iter_key_type)key_type);
407}
408
409U_CFUNC PHP_FUNCTION(breakiter_get_error_code)
410{
411    BREAKITER_METHOD_INIT_VARS;
412    object = getThis();
413
414    if (zend_parse_parameters_none() == FAILURE) {
415        intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
416            "breakiter_get_error_code: bad arguments", 0);
417        RETURN_FALSE;
418    }
419
420    /* Fetch the object (without resetting its last error code ). */
421    bio = Z_INTL_BREAKITERATOR_P(object);
422    if (bio == NULL)
423        RETURN_FALSE;
424
425    RETURN_LONG((zend_long)BREAKITER_ERROR_CODE(bio));
426}
427
428U_CFUNC PHP_FUNCTION(breakiter_get_error_message)
429{
430    zend_string* message = NULL;
431    BREAKITER_METHOD_INIT_VARS;
432    object = getThis();
433
434    if (zend_parse_parameters_none() == FAILURE) {
435        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
436            "breakiter_get_error_message: bad arguments", 0 );
437        RETURN_FALSE;
438    }
439
440
441    /* Fetch the object (without resetting its last error code ). */
442    bio = Z_INTL_BREAKITERATOR_P(object);
443    if (bio == NULL)
444        RETURN_FALSE;
445
446    /* Return last error message. */
447    message = intl_error_get_message(BREAKITER_ERROR_P(bio));
448    RETURN_STR(message);
449}
450