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