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