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