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