/* +----------------------------------------------------------------------+ | Copyright © The PHP Group and Contributors. | +----------------------------------------------------------------------+ | This source file is subject to the Modified BSD License that is | | bundled with this package in the file LICENSE, and is available | | through the World Wide Web at . | | | | SPDX-License-Identifier: BSD-3-Clause | +----------------------------------------------------------------------+ | Authors: Sara Golemon | +----------------------------------------------------------------------+ */ #include "zend_exceptions.h" #include #include #include #include #include extern "C" { #include "converter.h" #include "php_intl.h" #include "../intl_error.h" #include "../intl_common.h" } #include "converter_arginfo.h" typedef struct _php_converter_object { UConverter *src, *dest; zend_fcall_info_cache to_cache, from_cache; intl_error error; zend_object obj; } php_converter_object; static inline php_converter_object *php_converter_fetch_object(zend_object *obj) { return (php_converter_object *)((char*)(obj) - offsetof(php_converter_object, obj)); } #define Z_INTL_CONVERTER_P(zv) php_converter_fetch_object(Z_OBJ_P(zv)) static zend_class_entry *php_converter_ce; static zend_object_handlers php_converter_object_handlers; #define CONV_GET(pzv) (Z_INTL_CONVERTER_P((pzv))) #define THROW_UFAILURE(obj, error) php_converter_throw_failure(obj, error, \ "returned error " ZEND_LONG_FMT ": %s", (zend_long)error, u_errorName(error)) /* {{{ php_converter_throw_failure */ static inline void php_converter_throw_failure(php_converter_object *objval, UErrorCode error, const char *format, ...) { intl_error *err = objval ? &(objval->error) : NULL; char message[1024]; va_list vargs; va_start(vargs, format); vsnprintf(message, sizeof(message), format, vargs); va_end(vargs); intl_errors_set(err, error, message); } /* }}} */ /* {{{ php_converter_default_callback */ static void php_converter_default_callback(zval *return_value, zval *zobj, zend_long reason, zval *error) { /* Basic functionality so children can call parent::toUCallback() */ switch (reason) { case UCNV_UNASSIGNED: case UCNV_ILLEGAL: case UCNV_IRREGULAR: { php_converter_object *objval = (php_converter_object*)CONV_GET(zobj); char chars[127]; int8_t chars_len = sizeof(chars); UErrorCode uerror = U_ZERO_ERROR; if(!objval->src) { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Source Converter has not been initialized yet"); chars[0] = 0x1A; chars[1] = 0; chars_len = 1; ZEND_TRY_ASSIGN_REF_LONG(error, U_INVALID_STATE_ERROR); RETVAL_STRINGL(chars, chars_len); return; } /* Yes, this is fairly wasteful at first glance, * but considering that the alternative is to store * what's sent into setSubstChars() and the fact * that this is an extremely unlikely codepath * I'd rather take the CPU hit here, than waste time * storing a value I'm unlikely to use. */ ucnv_getSubstChars(objval->src, chars, &chars_len, &uerror); if (U_FAILURE(uerror)) { THROW_UFAILURE(objval, uerror); chars[0] = 0x1A; chars[1] = 0; chars_len = 1; } ZEND_TRY_ASSIGN_REF_LONG(error, uerror); RETVAL_STRINGL(chars, chars_len); } } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, toUCallback) { zend_long reason; zend_string *source, *codeUnits; zval *error; ZEND_PARSE_PARAMETERS_START(4, 4) Z_PARAM_LONG(reason) Z_PARAM_STR(source) Z_PARAM_STR(codeUnits) Z_PARAM_ZVAL(error) ZEND_PARSE_PARAMETERS_END(); php_converter_default_callback(return_value, ZEND_THIS, reason, error); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, fromUCallback) { zend_long reason; zval *source, *error; zend_long codePoint; ZEND_PARSE_PARAMETERS_START(4, 4) Z_PARAM_LONG(reason) Z_PARAM_ARRAY(source) Z_PARAM_LONG(codePoint) Z_PARAM_ZVAL(error) ZEND_PARSE_PARAMETERS_END(); php_converter_default_callback(return_value, ZEND_THIS, reason, error); } /* }}} */ /* {{{ php_converter_check_limits */ static inline bool php_converter_check_limits(php_converter_object *objval, zend_long available, zend_long needed) { if (available < needed) { php_converter_throw_failure(objval, U_BUFFER_OVERFLOW_ERROR, "Buffer overrun " ZEND_LONG_FMT " bytes needed, " ZEND_LONG_FMT " available", needed, available); return false; } return true; } /* }}} */ #define TARGET_CHECK(cnvargs, needed) php_converter_check_limits(objval, cnvargs->targetLimit - cnvargs->target, needed) /* {{{ php_converter_append_toUnicode_target */ static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicodeArgs *args, php_converter_object *objval) { switch (Z_TYPE_P(val)) { case IS_NULL: /* Code unit is being skipped */ return; case IS_LONG: { zend_long lval = Z_LVAL_P(val); if ((lval < 0) || (lval > 0x10FFFF)) { php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "Invalid codepoint U+%04lx", lval); return; } if (lval > 0xFFFF) { /* Supplemental planes U+010000 - U+10FFFF */ if (TARGET_CHECK(args, 2)) { int32_t offset = 0; U16_APPEND_UNSAFE(args->target, offset, lval); args->target += offset; } return; } /* Non-suggogate BMP codepoint */ if (TARGET_CHECK(args, 1)) { *(args->target++) = (UChar)lval; } return; } case IS_STRING: { const char *strval = Z_STRVAL_P(val); int i = 0, strlen = Z_STRLEN_P(val); while((i != strlen) && TARGET_CHECK(args, 1)) { UChar c; U8_NEXT(strval, i, strlen, c); *(args->target++) = c; } return; } case IS_ARRAY: { HashTable *ht = Z_ARRVAL_P(val); zval *tmpzval; ZEND_HASH_FOREACH_VAL(ht, tmpzval) { php_converter_append_toUnicode_target(tmpzval, args, objval); } ZEND_HASH_FOREACH_END(); return; } default: php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "toUCallback() specified illegal type for substitution character"); } } /* }}} */ /* {{{ php_converter_to_u_callback */ static void php_converter_to_u_callback(const void *context, UConverterToUnicodeArgs *args, const char *codeUnits, int32_t length, UConverterCallbackReason reason, UErrorCode *pErrorCode) { php_converter_object *objval = (php_converter_object*)context; zval retval; zval zargs[4]; ZVAL_LONG(&zargs[0], reason); if (args->source) { ZVAL_STRINGL(&zargs[1], args->source, args->sourceLimit - args->source); } else { ZVAL_EMPTY_STRING(&zargs[1]); } if (codeUnits) { ZVAL_STRINGL(&zargs[2], codeUnits, length); } else { ZVAL_EMPTY_STRING(&zargs[2]); } ZVAL_LONG(&zargs[3], *pErrorCode); ZVAL_MAKE_REF(&zargs[3]); zend_call_known_fcc(&objval->to_cache, &retval, 4, zargs, NULL); /* When no exception is thrown */ if (EXPECTED(!Z_ISUNDEF(retval))) { php_converter_append_toUnicode_target(&retval, args, objval); zval_ptr_dtor(&retval); } if (Z_TYPE(zargs[3]) == IS_LONG) { *pErrorCode = static_cast(Z_LVAL(zargs[3])); } else if (Z_ISREF(zargs[3]) && Z_TYPE_P(Z_REFVAL(zargs[3])) == IS_LONG) { *pErrorCode = static_cast(Z_LVAL_P(Z_REFVAL(zargs[3]))); } zval_ptr_dtor(&zargs[0]); zval_ptr_dtor(&zargs[1]); zval_ptr_dtor(&zargs[2]); zval_ptr_dtor(&zargs[3]); } /* }}} */ /* {{{ php_converter_append_fromUnicode_target */ static void php_converter_append_fromUnicode_target(zval *val, UConverterFromUnicodeArgs *args, php_converter_object *objval) { switch (Z_TYPE_P(val)) { case IS_NULL: /* Ignore */ return; case IS_LONG: if (TARGET_CHECK(args, 1)) { *(args->target++) = Z_LVAL_P(val); } return; case IS_STRING: { size_t vallen = Z_STRLEN_P(val); if (TARGET_CHECK(args, vallen)) { args->target = reinterpret_cast(zend_mempcpy(args->target, Z_STRVAL_P(val), vallen)); } return; } case IS_ARRAY: { HashTable *ht = Z_ARRVAL_P(val); zval *tmpzval; ZEND_HASH_FOREACH_VAL(ht, tmpzval) { php_converter_append_fromUnicode_target(tmpzval, args, objval); } ZEND_HASH_FOREACH_END(); return; } default: php_converter_throw_failure(objval, U_ILLEGAL_ARGUMENT_ERROR, "fromUCallback() specified illegal type for substitution character"); } } /* }}} */ /* {{{ php_converter_from_u_callback */ static void php_converter_from_u_callback(const void *context, UConverterFromUnicodeArgs *args, const UChar *codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason, UErrorCode *pErrorCode) { php_converter_object *objval = (php_converter_object*)context; zval retval; zval zargs[4]; ZVAL_LONG(&zargs[0], reason); array_init_size(&zargs[1], length); zend_hash_real_init_packed(Z_ARRVAL(zargs[1])); int i = 0; while (i < length) { UChar32 c; U16_NEXT(codeUnits, i, length, c); add_next_index_long(&zargs[1], c); } ZVAL_LONG(&zargs[2], codePoint); ZVAL_LONG(&zargs[3], *pErrorCode); ZVAL_MAKE_REF(&zargs[3]); zend_call_known_fcc(&objval->from_cache, &retval, 4, zargs, NULL); /* When no exception is thrown */ if (EXPECTED(!Z_ISUNDEF(retval))) { php_converter_append_fromUnicode_target(&retval, args, objval); zval_ptr_dtor(&retval); } if (Z_TYPE(zargs[3]) == IS_LONG) { *pErrorCode = static_cast(Z_LVAL(zargs[3])); } else if (Z_ISREF(zargs[3]) && Z_TYPE_P(Z_REFVAL(zargs[3])) == IS_LONG) { *pErrorCode = static_cast(Z_LVAL_P(Z_REFVAL(zargs[3]))); } zval_ptr_dtor(&zargs[0]); zval_ptr_dtor(&zargs[1]); zval_ptr_dtor(&zargs[2]); zval_ptr_dtor(&zargs[3]); } /* }}} */ /* {{{ php_converter_set_callbacks */ static inline bool php_converter_set_callbacks(php_converter_object *objval, UConverter *cnv) { bool ret = true; UErrorCode error = U_ZERO_ERROR; if (objval->obj.ce == php_converter_ce) { /* Short-circuit having to go through method calls and data marshalling * when we're using default behavior */ return true; } ucnv_setToUCallBack(cnv, (UConverterToUCallback)php_converter_to_u_callback, (const void*)objval, nullptr, nullptr, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); ret = false; } error = U_ZERO_ERROR; ucnv_setFromUCallBack(cnv, (UConverterFromUCallback)php_converter_from_u_callback, (const void*)objval, nullptr, nullptr, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); ret = false; } return ret; } /* }}} */ /* {{{ php_converter_set_encoding */ static bool php_converter_set_encoding(php_converter_object *objval, UConverter **pcnv, const char *enc, size_t enc_len) { UErrorCode error = U_ZERO_ERROR; UConverter *cnv = ucnv_open(enc, &error); if (error == U_AMBIGUOUS_ALIAS_WARNING) { UErrorCode getname_error = U_ZERO_ERROR; const char *actual_encoding = ucnv_getName(cnv, &getname_error); if (U_FAILURE(getname_error)) { /* Should never happen */ actual_encoding = "(unknown)"; } char *msg; spprintf(&msg, 0, "Ambiguous encoding specified, using %s", actual_encoding); intl_error_set(NULL, error, msg); efree(msg); } else if (U_FAILURE(error)) { if (objval) { THROW_UFAILURE(objval, error); } else { char *msg; spprintf(&msg, 0, "Error setting encoding: %d - %s", (int)error, u_errorName(error)); intl_error_set(NULL, error, msg); efree(msg); } return false; } if (objval && !php_converter_set_callbacks(objval, cnv)) { return false; } if (*pcnv) { ucnv_close(*pcnv); } *pcnv = cnv; return true; } /* }}} */ /* {{{ php_converter_do_set_encoding */ static void php_converter_do_set_encoding(UConverter **pcnv, INTERNAL_FUNCTION_PARAMETERS) { php_converter_object *objval = CONV_GET(ZEND_THIS); char *enc; size_t enc_len; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(enc, enc_len) ZEND_PARSE_PARAMETERS_END(); intl_errors_reset(&objval->error); RETURN_BOOL(php_converter_set_encoding(objval, pcnv, enc, enc_len)); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, setSourceEncoding) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_set_encoding(&(objval->src), INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, setDestinationEncoding) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_set_encoding(&(objval->dest), INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ php_converter_do_get_encoding */ static void php_converter_do_get_encoding(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) { const char *name; ZEND_PARSE_PARAMETERS_NONE(); intl_errors_reset(&objval->error); if (!cnv) { RETURN_NULL(); } name = ucnv_getName(cnv, &objval->error.code); if (U_FAILURE(objval->error.code)) { THROW_UFAILURE(objval, objval->error.code); RETURN_FALSE; } RETURN_STRING(name); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getSourceEncoding) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_get_encoding(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getDestinationEncoding) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_get_encoding(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ php_converter_do_get_type */ static void php_converter_do_get_type(php_converter_object *objval, UConverter *cnv, INTERNAL_FUNCTION_PARAMETERS) { UConverterType t; ZEND_PARSE_PARAMETERS_NONE(); intl_errors_reset(&objval->error); if (!cnv) { RETURN_NULL(); } t = ucnv_getType(cnv); if (U_FAILURE(objval->error.code)) { THROW_UFAILURE(objval, objval->error.code); RETURN_FALSE; } RETURN_LONG(t); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getSourceType) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_get_type(objval, objval->src, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getDestinationType) { php_converter_object *objval = CONV_GET(ZEND_THIS); php_converter_do_get_type(objval, objval->dest, INTERNAL_FUNCTION_PARAM_PASSTHRU); } /* }}} */ /* {{{ php_converter_resolve_callback */ static void php_converter_resolve_callback( zend_fcall_info_cache *fcc, zend_object *obj, const char *callback_name, size_t callback_name_len ) { zend_function *fn = reinterpret_cast(zend_hash_str_find_ptr_lc(&obj->ce->function_table, callback_name, callback_name_len)); ZEND_ASSERT(fn != nullptr); fcc->function_handler = fn; fcc->object = obj; fcc->called_scope = obj->ce; fcc->calling_scope = nullptr; fcc->closure = nullptr; } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, __construct) { php_converter_object *objval = CONV_GET(ZEND_THIS); char *src = "utf-8"; size_t src_len = sizeof("utf-8") - 1; char *dest = src; size_t dest_len = src_len; intl_error_reset(NULL); ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL Z_PARAM_STRING_OR_NULL(dest, dest_len) Z_PARAM_STRING_OR_NULL(src, src_len) ZEND_PARSE_PARAMETERS_END(); const bool old_use_exception = INTL_G(use_exceptions); const zend_long old_error_level = INTL_G(error_level); INTL_G(use_exceptions) = true; INTL_G(error_level) = 0; if (UNEXPECTED(!php_converter_set_encoding(objval, &(objval->src), src, src_len))) { ZEND_ASSERT(EG(exception)); goto cleanup; } if (UNEXPECTED(!php_converter_set_encoding(objval, &(objval->dest), dest, dest_len))) { ZEND_ASSERT(EG(exception)); goto cleanup; } php_converter_resolve_callback(&objval->to_cache, Z_OBJ_P(ZEND_THIS), ZEND_STRL("toUCallback")); php_converter_resolve_callback(&objval->from_cache, Z_OBJ_P(ZEND_THIS), ZEND_STRL("fromUCallback")); cleanup: INTL_G(use_exceptions) = old_use_exception; INTL_G(error_level) = old_error_level; } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, setSubstChars) { php_converter_object *objval = CONV_GET(ZEND_THIS); char *chars; size_t chars_len; int ret = 1; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(chars, chars_len) ZEND_PARSE_PARAMETERS_END(); intl_errors_reset(&objval->error); if (objval->src) { UErrorCode error = U_ZERO_ERROR; ucnv_setSubstChars(objval->src, chars, chars_len, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); ret = 0; } } else { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Source Converter has not been initialized yet"); ret = 0; } if (objval->dest) { UErrorCode error = U_ZERO_ERROR; ucnv_setSubstChars(objval->dest, chars, chars_len, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); ret = 0; } } else { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Destination Converter has not been initialized yet"); ret = 0; } RETURN_BOOL(ret); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getSubstChars) { php_converter_object *objval = CONV_GET(ZEND_THIS); char chars[127]; int8_t chars_len = sizeof(chars); UErrorCode error = U_ZERO_ERROR; ZEND_PARSE_PARAMETERS_NONE(); intl_errors_reset(&objval->error); if (!objval->src) { RETURN_NULL(); } /* src and dest get the same subst chars set, * so it doesn't really matter which one we read from */ ucnv_getSubstChars(objval->src, chars, &chars_len, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); RETURN_FALSE; } RETURN_STRINGL(chars, chars_len); } /* }}} */ /* {{{ php_converter_do_convert */ static zend_string* php_converter_do_convert(UConverter *dest_cnv, UConverter *src_cnv, const char *src, int32_t src_len, php_converter_object *objval ) { UErrorCode error = U_ZERO_ERROR; int32_t temp_len, ret_len; zend_string *ret; UChar *temp; if (!src_cnv || !dest_cnv) { php_converter_throw_failure(objval, U_INVALID_STATE_ERROR, "Internal converters not initialized"); return nullptr; } /* Get necessary buffer size first */ temp_len = 1 + ucnv_toUChars(src_cnv, NULL, 0, src, src_len, &error); if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) { THROW_UFAILURE(objval, error); return nullptr; } temp = reinterpret_cast(safe_emalloc(sizeof(UChar), temp_len, sizeof(UChar))); /* Convert to intermediate UChar* array */ error = U_ZERO_ERROR; temp_len = ucnv_toUChars(src_cnv, temp, temp_len, src, src_len, &error); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); efree(temp); return nullptr; } temp[temp_len] = 0; /* Get necessary output buffer size */ ret_len = ucnv_fromUChars(dest_cnv, NULL, 0, temp, temp_len, &error); if (U_FAILURE(error) && error != U_BUFFER_OVERFLOW_ERROR) { THROW_UFAILURE(objval, error); efree(temp); return nullptr; } ret = zend_string_alloc(ret_len, 0); /* Convert to final encoding */ error = U_ZERO_ERROR; ZSTR_LEN(ret) = ucnv_fromUChars(dest_cnv, ZSTR_VAL(ret), ret_len+1, temp, temp_len, &error); efree(temp); if (U_FAILURE(error)) { THROW_UFAILURE(objval, error); zend_string_efree(ret); return nullptr; } return ret; } /* }}} */ /* {{{ */ #define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1); PHP_METHOD(UConverter, reasonText) { zend_long reason; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(reason) ZEND_PARSE_PARAMETERS_END(); intl_error_reset(NULL); switch (reason) { UCNV_REASON_CASE(UNASSIGNED) UCNV_REASON_CASE(ILLEGAL) UCNV_REASON_CASE(IRREGULAR) UCNV_REASON_CASE(RESET) UCNV_REASON_CASE(CLOSE) UCNV_REASON_CASE(CLONE) default: zend_argument_value_error(1, "must be a UConverter::REASON_* constant"); RETURN_THROWS(); } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, convert) { php_converter_object *objval = CONV_GET(ZEND_THIS); char *str; size_t str_len; zend_string *ret; bool reverse = false; ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STRING(str, str_len) Z_PARAM_OPTIONAL Z_PARAM_BOOL(reverse) ZEND_PARSE_PARAMETERS_END(); intl_errors_reset(&objval->error); ret = php_converter_do_convert(reverse ? objval->src : objval->dest, reverse ? objval->dest : objval->src, str, str_len, objval); if (ret) { RETURN_NEW_STR(ret); } else { RETURN_FALSE; } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, transcode) { char *str, *src, *dest; size_t str_len, src_len, dest_len; zval *options = nullptr; UConverter *src_cnv = nullptr, *dest_cnv = nullptr; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(str, str_len) Z_PARAM_STRING(dest, dest_len) Z_PARAM_STRING(src, src_len) Z_PARAM_OPTIONAL Z_PARAM_ARRAY_OR_NULL(options) ZEND_PARSE_PARAMETERS_END(); intl_error_reset(NULL); if (php_converter_set_encoding(NULL, &src_cnv, src, src_len) && php_converter_set_encoding(NULL, &dest_cnv, dest, dest_len)) { zend_string *ret; UErrorCode error = U_ZERO_ERROR; if (options && zend_hash_num_elements(Z_ARRVAL_P(options))) { zval *tmpzval; if (U_SUCCESS(error) && (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; ucnv_setSubstChars(src_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); } if (U_SUCCESS(error) && (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; ucnv_setSubstChars(dest_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); } } if (U_SUCCESS(error) && (ret = php_converter_do_convert(dest_cnv, src_cnv, str, str_len, NULL)) != NULL) { RETVAL_NEW_STR(ret); } if (U_FAILURE(error)) { THROW_UFAILURE(NULL, error); RETVAL_FALSE; } } else { RETVAL_FALSE; } if (src_cnv) { ucnv_close(src_cnv); } if (dest_cnv) { ucnv_close(dest_cnv); } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getErrorCode) { php_converter_object *objval = CONV_GET(ZEND_THIS); ZEND_PARSE_PARAMETERS_NONE(); RETURN_LONG(intl_error_get_code(&(objval->error))); } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getErrorMessage) { php_converter_object *objval = CONV_GET(ZEND_THIS); ZEND_PARSE_PARAMETERS_NONE(); zend_string *message = intl_error_get_message(&(objval->error)); if (message) { RETURN_STR(message); } else { RETURN_NULL(); } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getAvailable) { int32_t i, count = ucnv_countAvailable(); ZEND_PARSE_PARAMETERS_NONE(); intl_error_reset(NULL); array_init_size(return_value, count); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); for(i = 0; i < count; i++) { const char *name = ucnv_getAvailableName(i); add_next_index_string(return_value, name); } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getAliases) { char *name; size_t name_len; UErrorCode error = U_ZERO_ERROR; uint16_t i, count; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_STRING(name, name_len) ZEND_PARSE_PARAMETERS_END(); intl_error_reset(NULL); count = ucnv_countAliases(name, &error); if (U_FAILURE(error)) { THROW_UFAILURE(NULL, error); RETURN_FALSE; } array_init_size(return_value, count); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); for(i = 0; i < count; i++) { const char *alias; error = U_ZERO_ERROR; alias = ucnv_getAlias(name, i, &error); if (U_FAILURE(error)) { THROW_UFAILURE(NULL, error); zend_array_destroy(Z_ARR_P(return_value)); RETURN_NULL(); } add_next_index_string(return_value, alias); } } /* }}} */ /* {{{ */ PHP_METHOD(UConverter, getStandards) { uint16_t i, count; ZEND_PARSE_PARAMETERS_NONE(); intl_error_reset(NULL); count = ucnv_countStandards(); array_init_size(return_value, count); zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); for(i = 0; i < count; i++) { UErrorCode error = U_ZERO_ERROR; const char *name = ucnv_getStandard(i, &error); if (U_FAILURE(error)) { THROW_UFAILURE(NULL, error); zend_array_destroy(Z_ARR_P(return_value)); RETURN_NULL(); } add_next_index_string(return_value, name); } } /* }}} */ /* {{{ Converter create/clone/destroy */ static void php_converter_free_object(zend_object *obj) { php_converter_object *objval = php_converter_fetch_object(obj); if (objval->src) { ucnv_close(objval->src); } if (objval->dest) { ucnv_close(objval->dest); } intl_error_reset(&objval->error); zend_object_std_dtor(obj); } static zend_object *php_converter_object_ctor(zend_class_entry *ce, php_converter_object **pobjval) { php_converter_object *objval; objval = reinterpret_cast(zend_object_alloc(sizeof(php_converter_object), ce)); zend_object_std_init(&objval->obj, ce); object_properties_init(&objval->obj, ce); intl_error_init(&(objval->error)); *pobjval = objval; return &objval->obj; } static zend_object *php_converter_create_object(zend_class_entry *ce) { php_converter_object *objval = nullptr; zend_object *retval = php_converter_object_ctor(ce, &objval); object_properties_init(&(objval->obj), ce); return retval; } static zend_object *php_converter_clone_object(zend_object *object) { const php_converter_object *oldobj = php_converter_fetch_object(object); php_converter_object *objval; zend_object *retval = php_converter_object_ctor(object->ce, &objval); UErrorCode error = U_ZERO_ERROR; #if U_ICU_VERSION_MAJOR_NUM > 70 objval->src = ucnv_clone(oldobj->src, &error); #else objval->src = ucnv_safeClone(oldobj->src, NULL, NULL, &error); #endif if (U_SUCCESS(error)) { error = U_ZERO_ERROR; #if U_ICU_VERSION_MAJOR_NUM > 70 objval->dest = ucnv_clone(oldobj->dest, &error); #else objval->dest = ucnv_safeClone(oldobj->dest, NULL, NULL, &error); #endif } if (U_FAILURE(error)) { zend_throw_error(NULL, "Failed to clone UConverter"); return retval; } /* Update contexts for converter error handlers */ php_converter_set_callbacks(objval, objval->src ); php_converter_set_callbacks(objval, objval->dest); zend_objects_clone_members(&(objval->obj), &(oldobj->obj)); /* Newly cloned object deliberately does not inherit error state from original object */ return retval; } /* }}} */ /* {{{ php_converter_minit */ U_CFUNC int php_converter_minit(INIT_FUNC_ARGS) { php_converter_ce = register_class_UConverter(); php_converter_ce->create_object = php_converter_create_object; php_converter_ce->default_object_handlers = &php_converter_object_handlers; memcpy(&php_converter_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); php_converter_object_handlers.offset = offsetof(php_converter_object, obj); php_converter_object_handlers.clone_obj = php_converter_clone_object; php_converter_object_handlers.free_obj = php_converter_free_object; return SUCCESS; } /* }}} */