xref: /PHP-7.4/ext/intl/converter/converter.c (revision 6d19acf5)
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: Sara Golemon <pollita@php.net>                              |
14    +----------------------------------------------------------------------+
15  */
16 
17 #include "converter.h"
18 #include "zend_exceptions.h"
19 
20 #include <unicode/utypes.h>
21 #include <unicode/utf8.h>
22 #include <unicode/utf16.h>
23 #include <unicode/ucnv.h>
24 #include <unicode/ustring.h>
25 
26 #include "../intl_error.h"
27 #include "../intl_common.h"
28 
29 typedef struct _php_converter_object {
30 	UConverter *src, *dest;
31 	zend_fcall_info to_cb, from_cb;
32 	zend_fcall_info_cache to_cache, from_cache;
33 	intl_error error;
34 	zend_object obj;
35 } php_converter_object;
36 
37 
php_converter_fetch_object(zend_object * obj)38 static inline php_converter_object *php_converter_fetch_object(zend_object *obj) {
39 	return (php_converter_object *)((char*)(obj) - XtOffsetOf(php_converter_object, obj));
40 }
41 #define Z_INTL_CONVERTER_P(zv) php_converter_fetch_object(Z_OBJ_P(zv))
42 
43 static zend_class_entry     *php_converter_ce;
44 static zend_object_handlers  php_converter_object_handlers;
45 
46 #define CONV_GET(pzv)  (Z_INTL_CONVERTER_P((pzv)))
47 #define THROW_UFAILURE(obj, fname, error) php_converter_throw_failure(obj, error, \
48                                           fname "() returned error " ZEND_LONG_FMT ": %s", (zend_long)error, u_errorName(error))
49 
50 /* {{{ php_converter_throw_failure */
php_converter_throw_failure(php_converter_object * objval,UErrorCode error,const char * format,...)51 static inline void php_converter_throw_failure(php_converter_object *objval, UErrorCode error, const char *format, ...) {
52 	intl_error *err = objval ? &(objval->error) : NULL;
53 	char message[1024];
54 	va_list vargs;
55 
56 	va_start(vargs, format);
57 	vsnprintf(message, sizeof(message), format, vargs);
58 	va_end(vargs);
59 
60 	intl_errors_set(err, error, message, 1);
61 }
62 /* }}} */
63 
64 /* {{{ php_converter_default_callback */
php_converter_default_callback(zval * return_value,zval * zobj,zend_long reason,zval * error)65 static void php_converter_default_callback(zval *return_value, zval *zobj, zend_long reason, zval *error) {
66 	/* Basic functionality so children can call parent::toUCallback() */
67 	switch (reason) {
68 		case UCNV_UNASSIGNED:
69 		case UCNV_ILLEGAL:
70 		case UCNV_IRREGULAR:
71 		{
72 			php_converter_object *objval = (php_converter_object*)CONV_GET(zobj);
73 			char chars[127];
74 			int8_t chars_len = sizeof(chars);
75 			UErrorCode uerror = U_ZERO_ERROR;
76             if(!objval->src) {
77                 php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Source Converter has not been initialized yet");
78 				chars[0] = 0x1A;
79 				chars[1] = 0;
80 				chars_len = 1;
81 				ZEND_TRY_ASSIGN_REF_LONG(error, U_INVALID_STATE_ERROR);
82                 RETVAL_STRINGL(chars, chars_len);
83                 return;
84             }
85 
86 			/* Yes, this is fairly wasteful at first glance,
87 			 * but considering that the alternative is to store
88 			 * what's sent into setSubstChars() and the fact
89 			 * that this is an extremely unlikely codepath
90 			 * I'd rather take the CPU hit here, than waste time
91 			 * storing a value I'm unlikely to use.
92 			 */
93 			ucnv_getSubstChars(objval->src, chars, &chars_len, &uerror);
94 			if (U_FAILURE(uerror)) {
95 				THROW_UFAILURE(objval, "ucnv_getSubstChars", uerror);
96 				chars[0] = 0x1A;
97 				chars[1] = 0;
98 				chars_len = 1;
99 			}
100 			ZEND_TRY_ASSIGN_REF_LONG(error, uerror);
101 			RETVAL_STRINGL(chars, chars_len);
102 		}
103 	}
104 }
105 /* }}} */
106 
107 /* {{{ proto void UConverter::toUCallback(int $reason,
108                                           string $source, string $codeUnits,
109                                           int &$error) */
110 ZEND_BEGIN_ARG_INFO_EX(php_converter_toUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
111 	ZEND_ARG_INFO(0, reason)
112 	ZEND_ARG_INFO(0, source)
113 	ZEND_ARG_INFO(0, codeUnits)
114 	ZEND_ARG_INFO(1, error)
115 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,toUCallback)116 static PHP_METHOD(UConverter, toUCallback) {
117 	zend_long reason;
118 	zval *source, *codeUnits, *error;
119 
120 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lzzz",
121 		&reason, &source, &codeUnits, &error) == FAILURE) {
122 		return;
123 	}
124 
125 	php_converter_default_callback(return_value, ZEND_THIS, reason, error);
126 }
127 /* }}} */
128 
129 /* {{{ proto void UConverter::fromUCallback(int $reason,
130                                             Array $source, int $codePoint,
131                                             int &$error) */
132 ZEND_BEGIN_ARG_INFO_EX(php_converter_fromUCallback_arginfo, 0, ZEND_RETURN_VALUE, 4)
133 	ZEND_ARG_INFO(0, reason)
134 	ZEND_ARG_INFO(0, source)
135 	ZEND_ARG_INFO(0, codePoint)
136 	ZEND_ARG_INFO(1, error)
137 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,fromUCallback)138 static PHP_METHOD(UConverter, fromUCallback) {
139 	zend_long reason;
140 	zval *source, *codePoint, *error;
141 
142 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lzzz",
143 		&reason, &source, &codePoint, &error) == FAILURE) {
144 		return;
145 	}
146 
147 	php_converter_default_callback(return_value, ZEND_THIS, reason, error);
148 }
149 /* }}} */
150 
151 /* {{{ php_converter_check_limits */
php_converter_check_limits(php_converter_object * objval,zend_long available,zend_long needed)152 static inline zend_bool php_converter_check_limits(php_converter_object *objval, zend_long available, zend_long needed) {
153 	if (available < needed) {
154 		php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR, "Buffer overrun " ZEND_LONG_FMT " bytes needed, " ZEND_LONG_FMT " available", needed, available);
155 		return 0;
156 	}
157 	return 1;
158 }
159 /* }}} */
160 
161 #define TARGET_CHECK(cnvargs, needed) php_converter_check_limits(objval, cnvargs->targetLimit - cnvargs->target, needed)
162 
163 /* {{{ php_converter_append_toUnicode_target */
php_converter_append_toUnicode_target(zval * val,UConverterToUnicodeArgs * args,php_converter_object * objval)164 static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicodeArgs *args, php_converter_object *objval) {
165 	switch (Z_TYPE_P(val)) {
166 		case IS_NULL:
167 			/* Code unit is being skipped */
168 			return;
169 		case IS_LONG:
170 		{
171 			zend_long lval = Z_LVAL_P(val);
172 			if ((lval < 0) || (lval > 0x10FFFF)) {
173 				php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "Invalid codepoint U+%04lx", lval);
174 				return;
175 			}
176 			if (lval > 0xFFFF) {
177 				/* Supplemental planes U+010000 - U+10FFFF */
178 				if (TARGET_CHECK(args, 2)) {
179 					/* TODO: Find the ICU call which does this properly */
180 					*(args->target++) = (UChar)(((lval - 0x10000) >> 10)   | 0xD800);
181 					*(args->target++) = (UChar)(((lval - 0x10000) & 0x3FF) | 0xDC00);
182 				}
183 				return;
184 			}
185 			/* Non-suggogate BMP codepoint */
186 			if (TARGET_CHECK(args, 1)) {
187 				*(args->target++) = (UChar)lval;
188 			}
189 			return;
190 		}
191 		case IS_STRING:
192 		{
193 			const char *strval = Z_STRVAL_P(val);
194 			int i = 0, strlen = Z_STRLEN_P(val);
195 
196 			while((i != strlen) && TARGET_CHECK(args, 1)) {
197 				UChar c;
198 				U8_NEXT(strval, i, strlen, c);
199 				*(args->target++) = c;
200 			}
201 			return;
202 		}
203 		case IS_ARRAY:
204 		{
205 			HashTable *ht = Z_ARRVAL_P(val);
206 			zval *tmpzval;
207 
208 			ZEND_HASH_FOREACH_VAL(ht, tmpzval) {
209 				php_converter_append_toUnicode_target(tmpzval, args, objval);
210 			} ZEND_HASH_FOREACH_END();
211 			return;
212 		}
213 		default:
214 			php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR,
215                                                     "toUCallback() specified illegal type for substitution character");
216 	}
217 }
218 /* }}} */
219 
220 /* {{{ php_converter_to_u_callback */
php_converter_to_u_callback(const void * context,UConverterToUnicodeArgs * args,const char * codeUnits,int32_t length,UConverterCallbackReason reason,UErrorCode * pErrorCode)221 static void php_converter_to_u_callback(const void *context,
222                                         UConverterToUnicodeArgs *args,
223                                         const char *codeUnits, int32_t length,
224                                         UConverterCallbackReason reason,
225                                         UErrorCode *pErrorCode) {
226 	php_converter_object *objval = (php_converter_object*)context;
227 	zval retval;
228 	zval zargs[4];
229 
230 	ZVAL_LONG(&zargs[0], reason);
231 	if (args->source) {
232 		ZVAL_STRINGL(&zargs[1], args->source, args->sourceLimit - args->source);
233 	} else {
234 		ZVAL_EMPTY_STRING(&zargs[1]);
235 	}
236 	if (codeUnits) {
237 		ZVAL_STRINGL(&zargs[2], codeUnits, length);
238 	} else {
239 		ZVAL_EMPTY_STRING(&zargs[2]);
240 	}
241 	ZVAL_LONG(&zargs[3], *pErrorCode);
242 
243 	objval->to_cb.param_count    = 4;
244 	objval->to_cb.params = zargs;
245 	objval->to_cb.retval = &retval;
246 	objval->to_cb.no_separation  = 0;
247 	if (zend_call_function(&(objval->to_cb), &(objval->to_cache)) == FAILURE) {
248 		/* Unlikely */
249 		php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR, "Unexpected failure calling toUCallback()");
250 	} else if (!Z_ISUNDEF(retval)) {
251 		php_converter_append_toUnicode_target(&retval, args, objval);
252 		zval_ptr_dtor(&retval);
253 	}
254 
255 	if (Z_TYPE(zargs[3]) == IS_LONG) {
256 		*pErrorCode = Z_LVAL(zargs[3]);
257 	} else if (Z_ISREF(zargs[3]) && Z_TYPE_P(Z_REFVAL(zargs[3])) == IS_LONG) {
258 		*pErrorCode = Z_LVAL_P(Z_REFVAL(zargs[3]));
259 	}
260 
261 	zval_ptr_dtor(&zargs[0]);
262 	zval_ptr_dtor(&zargs[1]);
263 	zval_ptr_dtor(&zargs[2]);
264 	zval_ptr_dtor(&zargs[3]);
265 }
266 /* }}} */
267 
268 /* {{{ php_converter_append_fromUnicode_target */
php_converter_append_fromUnicode_target(zval * val,UConverterFromUnicodeArgs * args,php_converter_object * objval)269 static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUnicodeArgs *args, php_converter_object *objval) {
270 	switch (Z_TYPE_P(val)) {
271 		case IS_NULL:
272 			/* Ignore */
273 			return;
274 		case IS_LONG:
275 			if (TARGET_CHECK(args, 1)) {
276 				*(args->target++) = Z_LVAL_P(val);
277 			}
278 			return;
279 		case IS_STRING:
280 		{
281 			size_t vallen = Z_STRLEN_P(val);
282 			if (TARGET_CHECK(args, vallen)) {
283 				memcpy(args->target, Z_STRVAL_P(val), vallen);
284 				args->target += vallen;
285 			}
286 			return;
287 		}
288 		case IS_ARRAY:
289 		{
290 			HashTable *ht = Z_ARRVAL_P(val);
291 			zval *tmpzval;
292 			ZEND_HASH_FOREACH_VAL(ht, tmpzval) {
293 				php_converter_append_fromUnicode_target(tmpzval, args, objval);
294 			} ZEND_HASH_FOREACH_END();
295 			return;
296 		}
297 		default:
298 			php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "fromUCallback() specified illegal type for substitution character");
299 	}
300 }
301 /* }}} */
302 
303 /* {{{ php_converter_from_u_callback */
php_converter_from_u_callback(const void * context,UConverterFromUnicodeArgs * args,const UChar * codeUnits,int32_t length,UChar32 codePoint,UConverterCallbackReason reason,UErrorCode * pErrorCode)304 static void php_converter_from_u_callback(const void *context,
305                                           UConverterFromUnicodeArgs *args,
306                                           const UChar *codeUnits, int32_t length, UChar32 codePoint,
307                                           UConverterCallbackReason reason,
308                                           UErrorCode *pErrorCode) {
309 	php_converter_object *objval = (php_converter_object*)context;
310 	zval retval;
311 	zval zargs[4];
312 	int i;
313 
314 	ZVAL_LONG(&zargs[0], reason);
315 	array_init(&zargs[1]);
316 	i = 0;
317 	while (i < length) {
318 		UChar32 c;
319 		U16_NEXT(codeUnits, i, length, c);
320 		add_next_index_long(&zargs[1], c);
321 	}
322 	ZVAL_LONG(&zargs[2], codePoint);
323 	ZVAL_LONG(&zargs[3], *pErrorCode);
324 
325 	objval->from_cb.param_count = 4;
326 	objval->from_cb.params = zargs;
327 	objval->from_cb.retval = &retval;
328 	objval->from_cb.no_separation  = 0;
329 	if (zend_call_function(&(objval->from_cb), &(objval->from_cache)) == FAILURE) {
330 		/* Unlikely */
331 		php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR, "Unexpected failure calling fromUCallback()");
332 	} else if (!Z_ISUNDEF(retval)) {
333 		php_converter_append_fromUnicode_target(&retval, args, objval);
334 		zval_ptr_dtor(&retval);
335 	}
336 
337 	if (Z_TYPE(zargs[3]) == IS_LONG) {
338 		*pErrorCode = Z_LVAL(zargs[3]);
339 	} else if (Z_ISREF(zargs[3]) && Z_TYPE_P(Z_REFVAL(zargs[3])) == IS_LONG) {
340 		*pErrorCode = Z_LVAL_P(Z_REFVAL(zargs[3]));
341 	}
342 
343 	zval_ptr_dtor(&zargs[0]);
344 	zval_ptr_dtor(&zargs[1]);
345 	zval_ptr_dtor(&zargs[2]);
346 	zval_ptr_dtor(&zargs[3]);
347 }
348 /* }}} */
349 
350 /* {{{ php_converter_set_callbacks */
php_converter_set_callbacks(php_converter_object * objval,UConverter * cnv)351 static inline zend_bool php_converter_set_callbacks(php_converter_object *objval, UConverter *cnv) {
352 	zend_bool ret = 1;
353 	UErrorCode error = U_ZERO_ERROR;
354 
355 	if (objval->obj.ce == php_converter_ce) {
356 		/* Short-circuit having to go through method calls and data marshalling
357 		 * when we're using default behavior
358 		 */
359 		return 1;
360 	}
361 
362 	ucnv_setToUCallBack(cnv, (UConverterToUCallback)php_converter_to_u_callback, (const void*)objval,
363                                  NULL, NULL, &error);
364 	if (U_FAILURE(error)) {
365 		THROW_UFAILURE(objval, "ucnv_setToUCallBack", error);
366 		ret = 0;
367 	}
368 
369 	error = U_ZERO_ERROR;
370 	ucnv_setFromUCallBack(cnv, (UConverterFromUCallback)php_converter_from_u_callback, (const void*)objval,
371                                     NULL, NULL, &error);
372 	if (U_FAILURE(error)) {
373 		THROW_UFAILURE(objval, "ucnv_setFromUCallBack", error);
374 		ret = 0;
375 	}
376 	return ret;
377 }
378 /* }}} */
379 
380 /* {{{ php_converter_set_encoding */
php_converter_set_encoding(php_converter_object * objval,UConverter ** pcnv,const char * enc,size_t enc_len)381 static zend_bool php_converter_set_encoding(php_converter_object *objval,
382                                             UConverter **pcnv,
383                                             const char *enc, size_t enc_len
384                                            ) {
385 	UErrorCode error = U_ZERO_ERROR;
386 	UConverter *cnv = ucnv_open(enc, &error);
387 
388 	if (error == U_AMBIGUOUS_ALIAS_WARNING) {
389 		UErrorCode getname_error = U_ZERO_ERROR;
390 		const char *actual_encoding = ucnv_getName(cnv, &getname_error);
391 		if (U_FAILURE(getname_error)) {
392 			/* Should never happen */
393 			actual_encoding = "(unknown)";
394 		}
395 		php_error_docref(NULL, E_WARNING, "Ambiguous encoding specified, using %s", actual_encoding);
396 	} else if (U_FAILURE(error)) {
397 		if (objval) {
398 			THROW_UFAILURE(objval, "ucnv_open", error);
399 		} else {
400 			php_error_docref(NULL, E_WARNING, "Error setting encoding: %d - %s", (int)error, u_errorName(error));
401 		}
402 		return 0;
403 	}
404 
405 	if (objval && !php_converter_set_callbacks(objval, cnv)) {
406 		return 0;
407 	}
408 
409 	if (*pcnv) {
410 		ucnv_close(*pcnv);
411 	}
412 	*pcnv = cnv;
413 	return 1;
414 }
415 /* }}} */
416 
417 /* {{{ php_converter_do_set_encoding */
418 ZEND_BEGIN_ARG_INFO_EX(php_converter_set_encoding_arginfo, 0, ZEND_RETURN_VALUE, 1)
419 	ZEND_ARG_INFO(0, encoding)
420 ZEND_END_ARG_INFO();
php_converter_do_set_encoding(UConverter ** pcnv,INTERNAL_FUNCTION_PARAMETERS)421 static void php_converter_do_set_encoding(UConverter **pcnv, INTERNAL_FUNCTION_PARAMETERS) {
422 	php_converter_object *objval = CONV_GET(ZEND_THIS);
423 	char *enc;
424 	size_t enc_len;
425 
426 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &enc, &enc_len) == FAILURE) {
427 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Bad arguments, "
428 				"expected one string argument", 0);
429 		RETURN_FALSE;
430 	}
431 	intl_errors_reset(&objval->error);
432 
433 	RETURN_BOOL(php_converter_set_encoding(objval, pcnv, enc, enc_len));
434 }
435 /* }}} */
436 
437 /* {{{ proto bool UConverter::setSourceEncoding(string encoding) */
PHP_METHOD(UConverter,setSourceEncoding)438 static PHP_METHOD(UConverter, setSourceEncoding) {
439 	php_converter_object *objval = CONV_GET(ZEND_THIS);
440 	php_converter_do_set_encoding(&(objval->src), INTERNAL_FUNCTION_PARAM_PASSTHRU);
441 }
442 /* }}} */
443 
444 /* {{{ proto bool UConverter::setDestinationEncoding(string encoding) */
PHP_METHOD(UConverter,setDestinationEncoding)445 static PHP_METHOD(UConverter, setDestinationEncoding) {
446 	php_converter_object *objval = CONV_GET(ZEND_THIS);
447 	php_converter_do_set_encoding(&(objval->dest), INTERNAL_FUNCTION_PARAM_PASSTHRU);
448 }
449 /* }}} */
450 
451 /* {{{ php_converter_do_get_encoding */
452 ZEND_BEGIN_ARG_INFO_EX(php_converter_get_encoding_arginfo, 0, ZEND_RETURN_VALUE, 0)
453 ZEND_END_ARG_INFO();
php_converter_do_get_encoding(php_converter_object * objval,UConverter * cnv,INTERNAL_FUNCTION_PARAMETERS)454 static void php_converter_do_get_encoding(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
455 	const char *name;
456 
457 	if (zend_parse_parameters_none() == FAILURE) {
458 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0);
459 		RETURN_FALSE;
460 	}
461 
462 	intl_errors_reset(&objval->error);
463 
464 	if (!cnv) {
465 		RETURN_NULL();
466 	}
467 
468 	name = ucnv_getName(cnv, &objval->error.code);
469 	if (U_FAILURE(objval->error.code)) {
470 		THROW_UFAILURE(objval, "ucnv_getName()", objval->error.code);
471 		RETURN_FALSE;
472 	}
473 
474 	RETURN_STRING(name);
475 }
476 /* }}} */
477 
478 /* {{{ proto string UConverter::getSourceEncoding() */
PHP_METHOD(UConverter,getSourceEncoding)479 static PHP_METHOD(UConverter, getSourceEncoding) {
480 	php_converter_object *objval = CONV_GET(ZEND_THIS);
481 	php_converter_do_get_encoding(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
482 }
483 /* }}} */
484 
485 /* {{{ proto string UConverter::getDestinationEncoding() */
PHP_METHOD(UConverter,getDestinationEncoding)486 static PHP_METHOD(UConverter, getDestinationEncoding) {
487         php_converter_object *objval = CONV_GET(ZEND_THIS);
488         php_converter_do_get_encoding(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
489 }
490 /* }}} */
491 
492 /* {{{ php_converter_do_get_type */
493 ZEND_BEGIN_ARG_INFO_EX(php_converter_get_type_arginfo, 0, ZEND_RETURN_VALUE, 0)
494 ZEND_END_ARG_INFO();
php_converter_do_get_type(php_converter_object * objval,UConverter * cnv,INTERNAL_FUNCTION_PARAMETERS)495 static void php_converter_do_get_type(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) {
496 	UConverterType t;
497 
498 	if (zend_parse_parameters_none() == FAILURE) {
499 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "Expected no arguments", 0);
500 		RETURN_FALSE;
501 	}
502 	intl_errors_reset(&objval->error);
503 
504 	if (!cnv) {
505 		RETURN_NULL();
506 	}
507 
508 	t = ucnv_getType(cnv);
509 	if (U_FAILURE(objval->error.code)) {
510 		THROW_UFAILURE(objval, "ucnv_getType", objval->error.code);
511 		RETURN_FALSE;
512 	}
513 
514 	RETURN_LONG(t);
515 }
516 /* }}} */
517 
518 /* {{{ proto int UConverter::getSourceType() */
PHP_METHOD(UConverter,getSourceType)519 static PHP_METHOD(UConverter, getSourceType) {
520 	php_converter_object *objval = CONV_GET(ZEND_THIS);
521 	php_converter_do_get_type(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU);
522 }
523 /* }}} */
524 
525 /* {{{ proto int UConverter::getDestinationType() */
PHP_METHOD(UConverter,getDestinationType)526 static PHP_METHOD(UConverter, getDestinationType) {
527 	php_converter_object *objval = CONV_GET(ZEND_THIS);
528 	php_converter_do_get_type(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU);
529 }
530 /* }}} */
531 
532 /* {{{ php_converter_resolve_callback */
php_converter_resolve_callback(zval * zobj,php_converter_object * objval,const char * callback_name,zend_fcall_info * finfo,zend_fcall_info_cache * fcache)533 static void php_converter_resolve_callback(zval *zobj,
534                                            php_converter_object *objval,
535                                            const char *callback_name,
536                                            zend_fcall_info *finfo,
537                                            zend_fcall_info_cache *fcache) {
538 	char *errstr = NULL;
539 	zval caller;
540 
541 	array_init(&caller);
542 	Z_ADDREF_P(zobj);
543 	add_index_zval(&caller, 0, zobj);
544 	add_index_string(&caller, 1, callback_name);
545 	if (zend_fcall_info_init(&caller, 0, finfo, fcache, NULL, &errstr) == FAILURE) {
546 		php_converter_throw_failure(objval, U_INTERNAL_PROGRAM_ERROR, "Error setting converter callback: %s", errstr);
547 	}
548 	zend_array_destroy(Z_ARR(caller));
549 	ZVAL_UNDEF(&finfo->function_name);
550 	if (errstr) {
551 		efree(errstr);
552 	}
553 }
554 /* }}} */
555 
556 /* {{{ proto UConverter::__construct([string dest = 'utf-8',[string src = 'utf-8']]) */
557 ZEND_BEGIN_ARG_INFO_EX(php_converter_arginfo, 0, ZEND_RETURN_VALUE, 0)
558 	ZEND_ARG_INFO(0, destination_encoding)
559 	ZEND_ARG_INFO(0, source_encoding)
560 ZEND_END_ARG_INFO();
561 
PHP_METHOD(UConverter,__construct)562 static PHP_METHOD(UConverter, __construct) {
563 	php_converter_object *objval = CONV_GET(ZEND_THIS);
564 	char *src = "utf-8";
565 	size_t src_len = sizeof("utf-8") - 1;
566 	char *dest = src;
567 	size_t dest_len = src_len;
568 
569 	intl_error_reset(NULL);
570 
571 	if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "|s!s!", &dest, &dest_len, &src, &src_len) == FAILURE) {
572 		return;
573 	}
574 
575 	php_converter_set_encoding(objval, &(objval->src),  src,  src_len );
576 	php_converter_set_encoding(objval, &(objval->dest), dest, dest_len);
577 	php_converter_resolve_callback(ZEND_THIS, objval, "toUCallback",   &(objval->to_cb),   &(objval->to_cache));
578 	php_converter_resolve_callback(ZEND_THIS, objval, "fromUCallback", &(objval->from_cb), &(objval->from_cache));
579 }
580 /* }}} */
581 
582 /* {{{ proto bool UConverter::setSubstChars(string $chars) */
583 ZEND_BEGIN_ARG_INFO_EX(php_converter_setSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 1)
584 	ZEND_ARG_INFO(0, chars)
585 ZEND_END_ARG_INFO();
586 
PHP_METHOD(UConverter,setSubstChars)587 static PHP_METHOD(UConverter, setSubstChars) {
588 	php_converter_object *objval = CONV_GET(ZEND_THIS);
589 	char *chars;
590 	size_t chars_len;
591 	int ret = 1;
592 
593 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &chars, &chars_len) == FAILURE) {
594 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
595 			"UConverter::setSubstChars(): bad arguments", 0);
596 		RETURN_FALSE;
597 	}
598 	intl_errors_reset(&objval->error);
599 
600 	if (objval->src) {
601 		UErrorCode error = U_ZERO_ERROR;
602 		ucnv_setSubstChars(objval->src, chars, chars_len, &error);
603 		if (U_FAILURE(error)) {
604 			THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
605 			ret = 0;
606 		}
607 	} else {
608 		php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Source Converter has not been initialized yet");
609 		ret = 0;
610 	}
611 
612 	if (objval->dest) {
613 		UErrorCode error = U_ZERO_ERROR;
614 		ucnv_setSubstChars(objval->dest, chars, chars_len, &error);
615 		if (U_FAILURE(error)) {
616 			THROW_UFAILURE(objval, "ucnv_setSubstChars", error);
617 			ret = 0;
618 		}
619 	} else {
620 		php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Destination Converter has not been initialized yet");
621 		ret = 0;
622 	}
623 
624 	RETURN_BOOL(ret);
625 }
626 /* }}} */
627 
628 /* {{{ proto string UConverter::getSubstChars() */
629 ZEND_BEGIN_ARG_INFO_EX(php_converter_getSubstChars_arginfo, 0, ZEND_RETURN_VALUE, 0)
630 ZEND_END_ARG_INFO();
631 
PHP_METHOD(UConverter,getSubstChars)632 static PHP_METHOD(UConverter, getSubstChars) {
633 	php_converter_object *objval = CONV_GET(ZEND_THIS);
634 	char chars[127];
635 	int8_t chars_len = sizeof(chars);
636 	UErrorCode error = U_ZERO_ERROR;
637 
638 	if (zend_parse_parameters_none() == FAILURE) {
639 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
640 			"UConverter::getSubstChars(): expected no arguments", 0);
641 		RETURN_FALSE;
642 	}
643 	intl_errors_reset(&objval->error);
644 
645 	if (!objval->src) {
646 		RETURN_NULL();
647 	}
648 
649 	/* src and dest get the same subst chars set,
650 	 * so it doesn't really matter which one we read from
651 	 */
652 	ucnv_getSubstChars(objval->src, chars, &chars_len, &error);
653 	if (U_FAILURE(error)) {
654 		THROW_UFAILURE(objval, "ucnv_getSubstChars", error);
655 		RETURN_FALSE;
656 	}
657 
658 	RETURN_STRINGL(chars, chars_len);
659 }
660 /* }}} */
661 
662 /* {{{ php_converter_do_convert */
php_converter_do_convert(UConverter * dest_cnv,UConverter * src_cnv,const char * src,int32_t src_len,php_converter_object * objval)663 static zend_string* php_converter_do_convert(UConverter *dest_cnv,
664                                              UConverter *src_cnv,  const char *src, int32_t src_len,
665                                              php_converter_object *objval
666                                             ) {
667 	UErrorCode	error = U_ZERO_ERROR;
668 	int32_t		temp_len, ret_len;
669 	zend_string	*ret;
670 	UChar		*temp;
671 
672 	if (!src_cnv || !dest_cnv) {
673 		php_converter_throw_failure(objval, U_INVALID_STATE_ERROR,
674 		                            "Internal converters not initialized");
675 		return NULL;
676 	}
677 
678 	/* Get necessary buffer size first */
679 	temp_len = 1 + ucnv_toUChars(src_cnv, NULL, 0, src, src_len, &error);
680 	if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
681 		THROW_UFAILURE(objval, "ucnv_toUChars", error);
682 		return NULL;
683 	}
684 	temp = safe_emalloc(sizeof(UChar), temp_len, sizeof(UChar));
685 
686 	/* Convert to intermediate UChar* array */
687 	error = U_ZERO_ERROR;
688 	temp_len = ucnv_toUChars(src_cnv, temp, temp_len, src, src_len, &error);
689 	if (U_FAILURE(error)) {
690 		THROW_UFAILURE(objval, "ucnv_toUChars", error);
691 		efree(temp);
692 		return NULL;
693 	}
694 	temp[temp_len] = 0;
695 
696 	/* Get necessary output buffer size */
697 	ret_len = ucnv_fromUChars(dest_cnv, NULL, 0, temp, temp_len, &error);
698 	if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) {
699 		THROW_UFAILURE(objval, "ucnv_fromUChars", error);
700 		efree(temp);
701 		return NULL;
702 	}
703 
704 	ret = zend_string_alloc(ret_len, 0);
705 
706 	/* Convert to final encoding */
707 	error = U_ZERO_ERROR;
708 	ZSTR_LEN(ret) = ucnv_fromUChars(dest_cnv, ZSTR_VAL(ret), ret_len+1, temp, temp_len, &error);
709 	efree(temp);
710 	if (U_FAILURE(error)) {
711 		THROW_UFAILURE(objval, "ucnv_fromUChars", error);
712 		zend_string_efree(ret);
713 		return NULL;
714 	}
715 
716 	return ret;
717 }
718 /* }}} */
719 
720 /* {{{ proto string UConverter::reasonText(int reason) */
721 #define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1);
722 ZEND_BEGIN_ARG_INFO_EX(php_converter_reasontext_arginfo, 0, ZEND_RETURN_VALUE, 0)
723 	ZEND_ARG_INFO(0, reason)
724 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,reasonText)725 static PHP_METHOD(UConverter, reasonText) {
726 	zend_long reason;
727 
728 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &reason) == FAILURE) {
729 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
730 			"UConverter::reasonText(): bad arguments", 0);
731 		RETURN_FALSE;
732 	}
733 	intl_error_reset(NULL);
734 
735 	switch (reason) {
736 		UCNV_REASON_CASE(UNASSIGNED)
737 		UCNV_REASON_CASE(ILLEGAL)
738 		UCNV_REASON_CASE(IRREGULAR)
739 		UCNV_REASON_CASE(RESET)
740 		UCNV_REASON_CASE(CLOSE)
741 		UCNV_REASON_CASE(CLONE)
742 		default:
743 			php_error_docref(NULL, E_WARNING, "Unknown UConverterCallbackReason: " ZEND_LONG_FMT, reason);
744 			RETURN_FALSE;
745 	}
746 }
747 /* }}} */
748 
749 /* {{{ proto string UConverter::convert(string str[, bool reverse]) */
750 ZEND_BEGIN_ARG_INFO_EX(php_converter_convert_arginfo, 0, ZEND_RETURN_VALUE, 1)
751         ZEND_ARG_INFO(0, str)
752 	ZEND_ARG_INFO(0, reverse)
753 ZEND_END_ARG_INFO();
754 
PHP_METHOD(UConverter,convert)755 static PHP_METHOD(UConverter, convert) {
756         php_converter_object *objval = CONV_GET(ZEND_THIS);
757 	char *str;
758 	size_t str_len;
759 	zend_string *ret;
760 	zend_bool reverse = 0;
761 
762 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b",
763 	                          &str, &str_len, &reverse) == FAILURE) {
764 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
765 			"UConverter::convert(): bad arguments", 0);
766 		RETURN_FALSE;
767 	}
768 	intl_errors_reset(&objval->error);
769 
770 	ret = php_converter_do_convert(reverse ? objval->src : objval->dest,
771 	                               reverse ? objval->dest : objval->src,
772 	                               str,   str_len,
773 	                               objval);
774 	if (ret) {
775 		RETURN_NEW_STR(ret);
776 	} else {
777 		RETURN_FALSE;
778 	}
779 }
780 /* }}} */
781 
782 /* {{{ proto string UConverter::transcode(string $str, string $toEncoding, string $fromEncoding[, Array $options = array()]) */
783 ZEND_BEGIN_ARG_INFO_EX(php_converter_transcode_arginfo, 0, ZEND_RETURN_VALUE, 3)
784 	ZEND_ARG_INFO(0, str)
785 	ZEND_ARG_INFO(0, toEncoding)
786 	ZEND_ARG_INFO(0, fromEncoding)
787 	ZEND_ARG_ARRAY_INFO(0, options, 1)
788 ZEND_END_ARG_INFO();
789 
PHP_METHOD(UConverter,transcode)790 static PHP_METHOD(UConverter, transcode) {
791 	char *str, *src, *dest;
792 	size_t str_len, src_len, dest_len;
793 	zval *options = NULL;
794 	UConverter *src_cnv = NULL, *dest_cnv = NULL;
795 
796 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a!",
797 			&str, &str_len, &dest, &dest_len, &src, &src_len, &options) == FAILURE) {
798 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
799 			"UConverter::transcode(): bad arguments", 0);
800 		RETURN_FALSE;
801 	}
802 	intl_error_reset(NULL);
803 
804 	if (php_converter_set_encoding(NULL, &src_cnv,  src,  src_len) &&
805 	    php_converter_set_encoding(NULL, &dest_cnv, dest, dest_len)) {
806 	    zend_string *ret;
807 		UErrorCode error = U_ZERO_ERROR;
808 
809 		if (options && zend_hash_num_elements(Z_ARRVAL_P(options))) {
810 			zval *tmpzval;
811 
812 			if (U_SUCCESS(error) &&
813 				(tmpzval = zend_hash_str_find(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst") - 1)) != NULL &&
814 				Z_TYPE_P(tmpzval) == IS_STRING) {
815 				error = U_ZERO_ERROR;
816 				ucnv_setSubstChars(src_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error);
817 			}
818 			if (U_SUCCESS(error) &&
819 				(tmpzval = zend_hash_str_find(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst") - 1)) != NULL &&
820 				Z_TYPE_P(tmpzval) == IS_STRING) {
821 				error = U_ZERO_ERROR;
822 				ucnv_setSubstChars(dest_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error);
823 			}
824 		}
825 
826 		if (U_SUCCESS(error) &&
827 			(ret = php_converter_do_convert(dest_cnv, src_cnv, str, str_len, NULL)) != NULL) {
828 			RETVAL_NEW_STR(ret);
829 		}
830 
831 		if (U_FAILURE(error)) {
832 			THROW_UFAILURE(NULL, "transcode", error);
833 			RETVAL_FALSE;
834 		}
835 	} else {
836 		RETVAL_FALSE;
837 	}
838 
839 	if (src_cnv) {
840 		ucnv_close(src_cnv);
841 	}
842 	if (dest_cnv) {
843 		ucnv_close(dest_cnv);
844 	}
845 }
846 /* }}} */
847 
848 /* {{{ proto int UConverter::getErrorCode() */
849 ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrorcode_arginfo, 0, ZEND_RETURN_VALUE, 0)
850 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,getErrorCode)851 static PHP_METHOD(UConverter, getErrorCode) {
852 	php_converter_object *objval = CONV_GET(ZEND_THIS);
853 
854 	if (zend_parse_parameters_none() == FAILURE) {
855 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
856 			"UConverter::getErrorCode(): expected no arguments", 0);
857 		RETURN_FALSE;
858 	}
859 
860 	RETURN_LONG(intl_error_get_code(&(objval->error)));
861 }
862 /* }}} */
863 
864 /* {{{ proto string UConverter::getErrorMessage() */
865 ZEND_BEGIN_ARG_INFO_EX(php_converter_geterrormsg_arginfo, 0, ZEND_RETURN_VALUE, 0)
866 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,getErrorMessage)867 static PHP_METHOD(UConverter, getErrorMessage) {
868 	php_converter_object *objval = CONV_GET(ZEND_THIS);
869 	zend_string *message = intl_error_get_message(&(objval->error));
870 
871 	if (zend_parse_parameters_none() == FAILURE) {
872 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
873 			"UConverter::getErrorMessage(): expected no arguments", 0);
874 		RETURN_FALSE;
875 	}
876 
877 	if (message) {
878 		RETURN_STR(message);
879 	} else {
880 		RETURN_NULL();
881 	}
882 }
883 /* }}} */
884 
885 /* {{{ proto array UConverter::getAvailable() */
886 ZEND_BEGIN_ARG_INFO_EX(php_converter_getavailable_arginfo, 0, ZEND_RETURN_VALUE, 0)
887 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,getAvailable)888 static PHP_METHOD(UConverter, getAvailable) {
889 	int32_t i,
890 			count = ucnv_countAvailable();
891 
892 	if (zend_parse_parameters_none() == FAILURE) {
893 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
894 			"UConverter::getErrorMessage(): expected no arguments", 0);
895 		RETURN_FALSE;
896 	}
897 	intl_error_reset(NULL);
898 
899 	array_init(return_value);
900 	for(i = 0; i < count; i++) {
901 		const char *name = ucnv_getAvailableName(i);
902 		add_next_index_string(return_value, name);
903 	}
904 }
905 /* }}} */
906 
907 /* {{{ proto array UConverter::getAliases(string name) */
908 ZEND_BEGIN_ARG_INFO_EX(php_converter_getaliases_arginfo, 0, ZEND_RETURN_VALUE, 1)
909 	ZEND_ARG_INFO(0, name)
910 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,getAliases)911 static PHP_METHOD(UConverter, getAliases) {
912 	char *name;
913 	size_t name_len;
914 	UErrorCode error = U_ZERO_ERROR;
915 	uint16_t i, count;
916 
917 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
918 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
919 			"UConverter::getAliases(): bad arguments", 0);
920 		RETURN_FALSE;
921 	}
922 	intl_error_reset(NULL);
923 
924 	count = ucnv_countAliases(name, &error);
925 	if (U_FAILURE(error)) {
926 		THROW_UFAILURE(NULL, "ucnv_countAliases", error);
927 		RETURN_FALSE;
928 	}
929 
930 	array_init(return_value);
931 	for(i = 0; i < count; i++) {
932 		const char *alias;
933 
934 		error = U_ZERO_ERROR;
935 		alias = ucnv_getAlias(name, i, &error);
936 		if (U_FAILURE(error)) {
937 			THROW_UFAILURE(NULL, "ucnv_getAlias", error);
938 			zend_array_destroy(Z_ARR_P(return_value));
939 			RETURN_NULL();
940 		}
941 		add_next_index_string(return_value, alias);
942 	}
943 }
944 /* }}} */
945 
946 /* {{{ proto array UConverter::getStandards() */
947 ZEND_BEGIN_ARG_INFO_EX(php_converter_getstandards_arginfo, 0, ZEND_RETURN_VALUE, 0)
948 ZEND_END_ARG_INFO();
PHP_METHOD(UConverter,getStandards)949 static PHP_METHOD(UConverter, getStandards) {
950 	uint16_t i, count;
951 
952 	if (zend_parse_parameters_none() == FAILURE) {
953 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
954 			"UConverter::getStandards(): expected no arguments", 0);
955 		RETURN_FALSE;
956 	}
957 	intl_error_reset(NULL);
958 
959 	array_init(return_value);
960 	count = ucnv_countStandards();
961 	for(i = 0; i < count; i++) {
962 		UErrorCode error = U_ZERO_ERROR;
963 		const char *name = ucnv_getStandard(i, &error);
964 		if (U_FAILURE(error)) {
965 			THROW_UFAILURE(NULL, "ucnv_getStandard", error);
966 			zend_array_destroy(Z_ARR_P(return_value));
967 			RETURN_NULL();
968 		}
969 		add_next_index_string(return_value, name);
970 	}
971 }
972 /* }}} */
973 
974 static const zend_function_entry php_converter_methods[] = {
975 	PHP_ME(UConverter, __construct,            php_converter_arginfo,                   ZEND_ACC_PUBLIC)
976 
977 	/* Encoding selection */
978 	PHP_ME(UConverter, setSourceEncoding,      php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
979 	PHP_ME(UConverter, setDestinationEncoding, php_converter_set_encoding_arginfo,      ZEND_ACC_PUBLIC)
980 	PHP_ME(UConverter, getSourceEncoding,      php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)
981 	PHP_ME(UConverter, getDestinationEncoding, php_converter_get_encoding_arginfo,      ZEND_ACC_PUBLIC)
982 
983 	/* Introspection for algorithmic converters */
984 	PHP_ME(UConverter, getSourceType,          php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)
985 	PHP_ME(UConverter, getDestinationType,     php_converter_get_type_arginfo,          ZEND_ACC_PUBLIC)
986 
987 	/* Basic codeunit error handling */
988 	PHP_ME(UConverter, getSubstChars,          php_converter_getSubstChars_arginfo,     ZEND_ACC_PUBLIC)
989 	PHP_ME(UConverter, setSubstChars,          php_converter_setSubstChars_arginfo,     ZEND_ACC_PUBLIC)
990 
991 	/* Default callback handlers */
992 	PHP_ME(UConverter, toUCallback,            php_converter_toUCallback_arginfo,       ZEND_ACC_PUBLIC)
993 	PHP_ME(UConverter, fromUCallback,          php_converter_fromUCallback_arginfo,     ZEND_ACC_PUBLIC)
994 
995 	/* Core conversion workhorses */
996 	PHP_ME(UConverter, convert,                php_converter_convert_arginfo,           ZEND_ACC_PUBLIC)
997 	PHP_ME(UConverter, transcode,              php_converter_transcode_arginfo,         ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
998 
999 	/* Error inspection */
1000 	PHP_ME(UConverter, getErrorCode,           php_converter_geterrorcode_arginfo,      ZEND_ACC_PUBLIC)
1001 	PHP_ME(UConverter, getErrorMessage,        php_converter_geterrormsg_arginfo,       ZEND_ACC_PUBLIC)
1002 
1003 	/* Ennumeration and lookup */
1004 	PHP_ME(UConverter, reasonText,             php_converter_reasontext_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1005 	PHP_ME(UConverter, getAvailable,           php_converter_getavailable_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1006 	PHP_ME(UConverter, getAliases,             php_converter_getaliases_arginfo,        ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1007 	PHP_ME(UConverter, getStandards,           php_converter_getstandards_arginfo,      ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
1008 	PHP_FE_END
1009 };
1010 
1011 /* {{{ Converter create/clone/destroy */
php_converter_dtor_object(zend_object * obj)1012 static void php_converter_dtor_object(zend_object *obj) {
1013 	php_converter_object *objval = php_converter_fetch_object(obj);
1014 
1015 	if (objval->src) {
1016 		ucnv_close(objval->src);
1017 	}
1018 
1019 	if (objval->dest) {
1020 		ucnv_close(objval->dest);
1021 	}
1022 
1023 	intl_error_reset(&(objval->error));
1024 }
1025 
php_converter_object_ctor(zend_class_entry * ce,php_converter_object ** pobjval)1026 static zend_object *php_converter_object_ctor(zend_class_entry *ce, php_converter_object **pobjval) {
1027 	php_converter_object *objval;
1028 
1029 	objval = zend_object_alloc(sizeof(php_converter_object), ce);
1030 
1031 	zend_object_std_init(&objval->obj, ce);
1032 	object_properties_init(&objval->obj, ce);
1033 	intl_error_init(&(objval->error));
1034 
1035 	objval->obj.handlers = &php_converter_object_handlers;
1036 	*pobjval = objval;
1037 
1038 	return &objval->obj;
1039 }
1040 
php_converter_create_object(zend_class_entry * ce)1041 static zend_object *php_converter_create_object(zend_class_entry *ce) {
1042 	php_converter_object *objval = NULL;
1043 	zend_object *retval = php_converter_object_ctor(ce, &objval);
1044 
1045 	object_properties_init(&(objval->obj), ce);
1046 
1047 	return retval;
1048 }
1049 
php_converter_clone_object(zval * object)1050 static zend_object *php_converter_clone_object(zval *object) {
1051 	php_converter_object *objval, *oldobj = Z_INTL_CONVERTER_P(object);
1052 	zend_object *retval = php_converter_object_ctor(Z_OBJCE_P(object), &objval);
1053 	UErrorCode error = U_ZERO_ERROR;
1054 
1055 	intl_errors_reset(&oldobj->error);
1056 
1057 	objval->src = ucnv_safeClone(oldobj->src, NULL, NULL, &error);
1058 	if (U_SUCCESS(error)) {
1059 		error = U_ZERO_ERROR;
1060 		objval->dest = ucnv_safeClone(oldobj->dest, NULL, NULL, &error);
1061 	}
1062 	if (U_FAILURE(error)) {
1063 		zend_string *err_msg;
1064 		THROW_UFAILURE(oldobj, "ucnv_safeClone", error);
1065 
1066 		err_msg = intl_error_get_message(&oldobj->error);
1067 		zend_throw_exception(NULL, ZSTR_VAL(err_msg), 0);
1068 		zend_string_release_ex(err_msg, 0);
1069 
1070 		return retval;
1071 	}
1072 
1073 	/* Update contexts for converter error handlers */
1074 	php_converter_set_callbacks(objval, objval->src );
1075 	php_converter_set_callbacks(objval, objval->dest);
1076 
1077 	zend_objects_clone_members(&(objval->obj), &(oldobj->obj));
1078 
1079 	/* Newly cloned object deliberately does not inherit error state from original object */
1080 
1081 	return retval;
1082 }
1083 /* }}} */
1084 
1085 #define CONV_REASON_CONST(v) zend_declare_class_constant_long(php_converter_ce, "REASON_" #v, sizeof("REASON_" #v) - 1, UCNV_ ## v)
1086 #define CONV_TYPE_CONST(v)   zend_declare_class_constant_long(php_converter_ce, #v ,          sizeof(#v) - 1,           UCNV_ ## v)
1087 
1088 /* {{{ php_converter_minit */
php_converter_minit(INIT_FUNC_ARGS)1089 int php_converter_minit(INIT_FUNC_ARGS) {
1090 	zend_class_entry ce;
1091 
1092 	INIT_CLASS_ENTRY(ce, "UConverter", php_converter_methods);
1093 	php_converter_ce = zend_register_internal_class(&ce);
1094 	php_converter_ce->create_object = php_converter_create_object;
1095 	memcpy(&php_converter_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1096 	php_converter_object_handlers.offset = XtOffsetOf(php_converter_object, obj);
1097 	php_converter_object_handlers.clone_obj = php_converter_clone_object;
1098 	php_converter_object_handlers.dtor_obj = php_converter_dtor_object;
1099 
1100 	/* enum UConverterCallbackReason */
1101 	CONV_REASON_CONST(UNASSIGNED);
1102 	CONV_REASON_CONST(ILLEGAL);
1103 	CONV_REASON_CONST(IRREGULAR);
1104 	CONV_REASON_CONST(RESET);
1105 	CONV_REASON_CONST(CLOSE);
1106 	CONV_REASON_CONST(CLONE);
1107 
1108 	/* enum UConverterType */
1109 	CONV_TYPE_CONST(UNSUPPORTED_CONVERTER);
1110 	CONV_TYPE_CONST(SBCS);
1111 	CONV_TYPE_CONST(DBCS);
1112 	CONV_TYPE_CONST(MBCS);
1113 	CONV_TYPE_CONST(LATIN_1);
1114 	CONV_TYPE_CONST(UTF8);
1115 	CONV_TYPE_CONST(UTF16_BigEndian);
1116 	CONV_TYPE_CONST(UTF16_LittleEndian);
1117 	CONV_TYPE_CONST(UTF32_BigEndian);
1118 	CONV_TYPE_CONST(UTF32_LittleEndian);
1119 	CONV_TYPE_CONST(EBCDIC_STATEFUL);
1120 	CONV_TYPE_CONST(ISO_2022);
1121 	CONV_TYPE_CONST(LMBCS_1);
1122 	CONV_TYPE_CONST(LMBCS_2);
1123 	CONV_TYPE_CONST(LMBCS_3);
1124 	CONV_TYPE_CONST(LMBCS_4);
1125 	CONV_TYPE_CONST(LMBCS_5);
1126 	CONV_TYPE_CONST(LMBCS_6);
1127 	CONV_TYPE_CONST(LMBCS_8);
1128 	CONV_TYPE_CONST(LMBCS_11);
1129 	CONV_TYPE_CONST(LMBCS_16);
1130 	CONV_TYPE_CONST(LMBCS_17);
1131 	CONV_TYPE_CONST(LMBCS_18);
1132 	CONV_TYPE_CONST(LMBCS_19);
1133 	CONV_TYPE_CONST(LMBCS_LAST);
1134 	CONV_TYPE_CONST(HZ);
1135 	CONV_TYPE_CONST(SCSU);
1136 	CONV_TYPE_CONST(ISCII);
1137 	CONV_TYPE_CONST(US_ASCII);
1138 	CONV_TYPE_CONST(UTF7);
1139 	CONV_TYPE_CONST(BOCU1);
1140 	CONV_TYPE_CONST(UTF16);
1141 	CONV_TYPE_CONST(UTF32);
1142 	CONV_TYPE_CONST(CESU8);
1143 	CONV_TYPE_CONST(IMAP_MAILBOX);
1144 
1145 	return SUCCESS;
1146 }
1147 /* }}} */
1148