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