diff options
Diffstat (limited to 'ext/intl')
65 files changed, 3258 insertions, 70 deletions
diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c index 38b2e978ce..d1fa10ef2c 100644 --- a/ext/intl/collator/collator_class.c +++ b/ext/intl/collator/collator_class.c @@ -67,6 +67,7 @@ zend_object_value Collator_object_create( intern = ecalloc( 1, sizeof(Collator_object) ); intl_error_init( COLLATOR_ERROR_P( intern ) TSRMLS_CC ); zend_object_std_init( &intern->zo, ce TSRMLS_CC ); + object_properties_init(&intern->zo, ce); retval.handle = zend_objects_store_put( intern, @@ -112,7 +113,7 @@ ZEND_END_ARG_INFO() * Every 'Collator' class method has an entry in this table */ -function_entry Collator_class_functions[] = { +zend_function_entry Collator_class_functions[] = { PHP_ME( Collator, __construct, collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) ZEND_FENTRY( create, ZEND_FN( collator_create ), collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) PHP_NAMED_FE( compare, ZEND_FN( collator_compare ), collator_2_args ) diff --git a/ext/intl/common/common_error.c b/ext/intl/common/common_error.c index 3ab7fdfbdd..a0ee7c145f 100644 --- a/ext/intl/common/common_error.c +++ b/ext/intl/common/common_error.c @@ -24,7 +24,7 @@ #include "common_error.h" /* {{{ proto int intl_get_error_code() - * Get code of the last occured error. + * Get code of the last occurred error. */ PHP_FUNCTION( intl_get_error_code ) { @@ -33,7 +33,7 @@ PHP_FUNCTION( intl_get_error_code ) /* }}} */ /* {{{ proto string intl_get_error_message() - * Get text description of the last occured error. + * Get text description of the last occurred error. */ PHP_FUNCTION( intl_get_error_message ) { @@ -232,7 +232,6 @@ void intl_expose_icu_error_codes( INIT_FUNC_ARGS ) INTL_EXPOSE_CONST( U_REGEX_ERROR_LIMIT ); /* The error code in the range 0x10400-0x104ff are reserved for IDNA related error codes */ -#if defined(U_IDNA_PROHIBITED_ERROR) INTL_EXPOSE_CONST( U_IDNA_PROHIBITED_ERROR ); INTL_EXPOSE_CONST( U_IDNA_ERROR_START ); INTL_EXPOSE_CONST( U_IDNA_UNASSIGNED_ERROR ); @@ -242,8 +241,10 @@ void intl_expose_icu_error_codes( INIT_FUNC_ARGS ) INTL_EXPOSE_CONST( U_IDNA_VERIFICATION_ERROR ); INTL_EXPOSE_CONST( U_IDNA_LABEL_TOO_LONG_ERROR ); INTL_EXPOSE_CONST( U_IDNA_ZERO_LENGTH_LABEL_ERROR ); - INTL_EXPOSE_CONST( U_IDNA_ERROR_LIMIT ); +#if U_ICU_VERSION_MAJOR_NUM > 3 || U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM >= 8 + INTL_EXPOSE_CONST( U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR ); #endif + INTL_EXPOSE_CONST( U_IDNA_ERROR_LIMIT ); /* Aliases for StringPrep */ INTL_EXPOSE_CONST( U_STRINGPREP_PROHIBITED_ERROR ); diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 index dc235db08c..0477c7f59d 100644 --- a/ext/intl/config.m4 +++ b/ext/intl/config.m4 @@ -9,7 +9,14 @@ if test "$PHP_INTL" != "no"; then PHP_SETUP_ICU(INTL_SHARED_LIBADD) PHP_SUBST(INTL_SHARED_LIBADD) PHP_REQUIRE_CXX() - + if test "$icu_version" -ge "4002"; then + icu_spoof_src=" spoofchecker/spoofchecker_class.c \ + spoofchecker/spoofchecker.c\ + spoofchecker/spoofchecker_create.c\ + spoofchecker/spoofchecker_main.c" + else + icu_spoof_src="" + fi PHP_NEW_EXTENSION(intl, php_intl.c \ intl_error.c \ intl_convert.c \ @@ -55,8 +62,11 @@ if test "$PHP_INTL" != "no"; then resourcebundle/resourcebundle.c \ resourcebundle/resourcebundle_class.c \ resourcebundle/resourcebundle_iterator.c \ - idn/idn.c, $ext_shared,,$ICU_INCS) - + transliterator/transliterator.c \ + transliterator/transliterator_class.c \ + transliterator/transliterator_methods.c \ + idn/idn.c \ + $icu_spoof_src, $ext_shared,,$ICU_INCS) PHP_ADD_BUILD_DIR($ext_builddir/collator) PHP_ADD_BUILD_DIR($ext_builddir/common) PHP_ADD_BUILD_DIR($ext_builddir/formatter) @@ -66,5 +76,7 @@ if test "$PHP_INTL" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/msgformat) PHP_ADD_BUILD_DIR($ext_builddir/grapheme) PHP_ADD_BUILD_DIR($ext_builddir/resourcebundle) + PHP_ADD_BUILD_DIR($ext_builddir/transliterator) PHP_ADD_BUILD_DIR($ext_builddir/idn) + PHP_ADD_BUILD_DIR($ext_builddir/spoofchecker) fi diff --git a/ext/intl/config.w32 b/ext/intl/config.w32 index 68ccadf2d8..437fedb7d3 100644 --- a/ext/intl/config.w32 +++ b/ext/intl/config.w32 @@ -71,6 +71,22 @@ if (PHP_INTL != "no") { resourcebundle_class.c \ resourcebundle_iterator.c", "intl"); + + if (CHECK_HEADER_ADD_INCLUDE("unicode/uspoof.h", "CFLAGS_INTL")) { + ADD_SOURCES(configure_module_dirname + "/spoofchecker", "\ + spoofchecker.c \ + spoofchecker_class.c \ + spoofchecker_create.c \ + spoofchecker_main.c", + "intl"); + AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled"); + } + + ADD_SOURCES(configure_module_dirname + "/transliterator", "\ + transliterator.c \ + transliterator_class.c \ + transliterator_methods.c", + "intl"); ADD_FLAG("LIBS_INTL", "icudt.lib icuin.lib icuio.lib icule.lib iculx.lib"); AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled"); } else { diff --git a/ext/intl/dateformat/dateformat_class.c b/ext/intl/dateformat/dateformat_class.c index eb3f5f4e77..a9e06c147d 100644 --- a/ext/intl/dateformat/dateformat_class.c +++ b/ext/intl/dateformat/dateformat_class.c @@ -63,6 +63,7 @@ zend_object_value IntlDateFormatter_object_create(zend_class_entry *ce TSRMLS_DC intern = ecalloc( 1, sizeof(IntlDateFormatter_object) ); dateformat_data_init( &intern->datef_data TSRMLS_CC ); zend_object_std_init( &intern->zo, ce TSRMLS_CC ); + object_properties_init(&intern->zo, ce); intern->date_type = 0; intern->time_type = 0; intern->calendar = 1; /* Gregorian calendar */ @@ -151,7 +152,7 @@ ZEND_END_ARG_INFO() /* {{{ IntlDateFormatter_class_functions * Every 'IntlDateFormatter' class method has an entry in this table */ -static function_entry IntlDateFormatter_class_functions[] = { +static zend_function_entry IntlDateFormatter_class_functions[] = { PHP_ME( IntlDateFormatter, __construct, arginfo_intldateformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) ZEND_FENTRY( create, ZEND_FN( datefmt_create ), arginfo_intldateformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) PHP_NAMED_FE( getDateType, ZEND_FN( datefmt_get_datetype ), arginfo_intldateformatter_getdatetype ) diff --git a/ext/intl/doc/collator_api.php b/ext/intl/doc/collator_api.php index 5428783750..ef7250243b 100644 --- a/ext/intl/doc/collator_api.php +++ b/ext/intl/doc/collator_api.php @@ -116,7 +116,7 @@ class Collator { /** * Return error text for the last ICU operation. * - * @return string Description of an error occured in the last + * @return string Description of an error occurred in the last * Collator method call. */ public function getErrorMessage() {} @@ -236,7 +236,7 @@ class Collator { * * @return string Real locale name from which the * collation data comes. If the collator - * was instantiated from rules or an error occured, + * was instantiated from rules or an error occurred, * returns false. */ public function getLocale( $type ) {} @@ -331,7 +331,7 @@ function collator_sort_with_sort_keys( $coll, $arr ) {} * * @return string Real locale name from which the * collation data comes. If the collator - * was instantiated from rules or an error occured, + * was instantiated from rules or an error occurred, * returns false. */ function collator_get_locale( $coll, $type ) {} @@ -391,7 +391,7 @@ function collator_get_error_code( $coll ) {} * * @param Collator $coll Collator object. * - * @return string Description of an error occured in the last + * @return string Description of an error occurred in the last * Collator API function call. */ function collator_get_error_message( $coll ) {} diff --git a/ext/intl/doc/common_api.php b/ext/intl/doc/common_api.php index ce2329fe3b..993dab180b 100644 --- a/ext/intl/doc/common_api.php +++ b/ext/intl/doc/common_api.php @@ -1,7 +1,7 @@ <?php /** - * Handling of errors occured in static methods + * Handling of errors occurred in static methods * when there's no object to get error code/message from. * * Example #1: @@ -29,7 +29,7 @@ function intl_get_error_code() {} /** * Get description of the last error. * - * @return string Description of an error occured in the last + * @return string Description of an error occurred in the last * API function call. */ function intl_get_error_message() {} diff --git a/ext/intl/doc/datefmt_api.php b/ext/intl/doc/datefmt_api.php index 0c5002e281..272abdb57c 100644 --- a/ext/intl/doc/datefmt_api.php +++ b/ext/intl/doc/datefmt_api.php @@ -215,7 +215,7 @@ class DateFormatter { /** * Sets the pattern to use * @param string $pattern new pattern string to use - * @return boolean 'true' if successful, 'false' if an error occured. Bad format + * @return boolean 'true' if successful, 'false' if an error occurred. Bad format * strings are usually the cause of the latter. */ public function setPattern($pattern) {} @@ -410,7 +410,7 @@ class DateFormatter { * Sets the pattern to use * @param DateFormatter $fmt The date formatter resource * @param string $pattern new pattern string to use - * @return boolean 'true' if successful, 'false' if an error occured. Bad format + * @return boolean 'true' if successful, 'false' if an error occurred. Bad format * strings are usually the cause of the latter. */ function datefmt_set_pattern($fmt , $pattern) {} diff --git a/ext/intl/doc/formatter_api.php b/ext/intl/doc/formatter_api.php index 754b16c62f..14d98db66a 100644 --- a/ext/intl/doc/formatter_api.php +++ b/ext/intl/doc/formatter_api.php @@ -317,7 +317,7 @@ class NumberFormatter { /** * Get the error text from the last operation. * - * @return string Description of the last occured error. + * @return string Description of the last occurred error. */ public public function getErrorMessage() {} @@ -495,6 +495,6 @@ function numfmt_get_error_code($formatter) {} * Get the error text from the last operation. * * @param NumberFormatter $formatter The formatter resource - * @return string Description of the last occured error. + * @return string Description of the last occurred error. */ function numfmt_get_error_message($formatter) {} diff --git a/ext/intl/doc/msgfmt_api.php b/ext/intl/doc/msgfmt_api.php index 5d178f1273..e4d047b979 100644 --- a/ext/intl/doc/msgfmt_api.php +++ b/ext/intl/doc/msgfmt_api.php @@ -54,7 +54,7 @@ class MessageFormatter { * @param string $locale the locale to use when formatting numbers and dates and suchlike * @param string $pattern the pattern string to insert things into * @param array $args the array of values to insert into $pattern - * @return string the formatted pattern string or false if an error occured + * @return string the formatted pattern string or false if an error occurred */ public static function formatMessage($locale, $pattern, $args) {} @@ -148,7 +148,7 @@ class MessageFormatter { * @param string $locale the locale to use when formatting numbers and dates and suchlike * @param string $pattern the pattern string to insert things into * @param array $args the array of values to insert into $pattern - * @return string the formatted pattern string or false if an error occured + * @return string the formatted pattern string or false if an error occurred */ function msgfmt_format_message($locale, $pattern, $args) {} diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c index 0bb5894f09..28af14e5db 100644 --- a/ext/intl/formatter/formatter_class.c +++ b/ext/intl/formatter/formatter_class.c @@ -62,6 +62,7 @@ zend_object_value NumberFormatter_object_create(zend_class_entry *ce TSRMLS_DC) intern = ecalloc( 1, sizeof(NumberFormatter_object) ); formatter_data_init( &intern->nf_data TSRMLS_CC ); zend_object_std_init( &intern->zo, ce TSRMLS_CC ); + object_properties_init(&intern->zo, ce); retval.handle = zend_objects_store_put( intern, @@ -161,7 +162,7 @@ ZEND_END_ARG_INFO() /* {{{ NumberFormatter_class_functions * Every 'NumberFormatter' class method has an entry in this table */ -static function_entry NumberFormatter_class_functions[] = { +static zend_function_entry NumberFormatter_class_functions[] = { PHP_ME( NumberFormatter, __construct, arginfo_numberformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) ZEND_FENTRY( create, ZEND_FN( numfmt_create ), arginfo_numberformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) PHP_NAMED_FE( format, ZEND_FN( numfmt_format ), arginfo_numberformatter_format ) diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c index 0b7ecdb27d..475bbe4184 100644 --- a/ext/intl/grapheme/grapheme_string.c +++ b/ext/intl/grapheme/grapheme_string.c @@ -554,13 +554,17 @@ PHP_FUNCTION(grapheme_substr) length += iter_val; } - if ( UBRK_DONE == sub_str_end_pos && length < 0) { + if ( UBRK_DONE == sub_str_end_pos) { + if(length < 0) { - intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: length not contained in string", 1 TSRMLS_CC ); + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: length not contained in string", 1 TSRMLS_CC ); - efree(ustr); - ubrk_close(bi); - RETURN_FALSE; + efree(ustr); + ubrk_close(bi); + RETURN_FALSE; + } else { + sub_str_end_pos = ustr_len; + } } sub_str = NULL; diff --git a/ext/intl/idn/idn.c b/ext/intl/idn/idn.c index 23cd0ea872..6332488cc9 100644 --- a/ext/intl/idn/idn.c +++ b/ext/intl/idn/idn.c @@ -13,6 +13,7 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Pierre A. Joye <pierre@php.net> | + | Gustavo Lopes <cataphract@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ @@ -29,14 +30,25 @@ #include "ext/standard/php_string.h" #include "intl_error.h" - #include "intl_convert.h" +#include "intl_convert.h" /* }}} */ +#ifdef UIDNA_INFO_INITIALIZER +#define HAVE_46_API 1 /* has UTS#46 API (introduced in ICU 4.6) */ +#endif + +enum { + INTL_IDN_VARIANT_2003 = 0, + INTL_IDN_VARIANT_UTS46 +}; + /* {{{ grapheme_register_constants * Register API constants */ void idn_register_constants( INIT_FUNC_ARGS ) { + /* OPTIONS */ + /* Option to prohibit processing of unassigned codepoints in the input and do not check if the input conforms to STD-3 ASCII rules. */ REGISTER_LONG_CONSTANT("IDNA_DEFAULT", UIDNA_DEFAULT, CONST_CS | CONST_PERSISTENT); @@ -46,6 +58,50 @@ void idn_register_constants( INIT_FUNC_ARGS ) /* Option to check if input conforms to STD-3 ASCII rules */ REGISTER_LONG_CONSTANT("IDNA_USE_STD3_RULES", UIDNA_USE_STD3_RULES, CONST_CS | CONST_PERSISTENT); + +#ifdef HAVE_46_API + + /* Option to check for whether the input conforms to the BiDi rules. + * Ignored by the IDNA2003 implementation. (IDNA2003 always performs a BiDi check.) */ + REGISTER_LONG_CONSTANT("IDNA_CHECK_BIDI", UIDNA_CHECK_BIDI, CONST_CS | CONST_PERSISTENT); + + /* Option to check for whether the input conforms to the CONTEXTJ rules. + * Ignored by the IDNA2003 implementation. (The CONTEXTJ check is new in IDNA2008.) */ + REGISTER_LONG_CONSTANT("IDNA_CHECK_CONTEXTJ", UIDNA_CHECK_CONTEXTJ, CONST_CS | CONST_PERSISTENT); + + /* Option for nontransitional processing in ToASCII(). + * By default, ToASCII() uses transitional processing. + * Ignored by the IDNA2003 implementation. */ + REGISTER_LONG_CONSTANT("IDNA_NONTRANSITIONAL_TO_ASCII", UIDNA_NONTRANSITIONAL_TO_ASCII, CONST_CS | CONST_PERSISTENT); + + /* Option for nontransitional processing in ToUnicode(). + * By default, ToUnicode() uses transitional processing. + * Ignored by the IDNA2003 implementation. */ + REGISTER_LONG_CONSTANT("IDNA_NONTRANSITIONAL_TO_UNICODE", UIDNA_NONTRANSITIONAL_TO_UNICODE, CONST_CS | CONST_PERSISTENT); +#endif + + /* VARIANTS */ + REGISTER_LONG_CONSTANT("INTL_IDNA_VARIANT_2003", INTL_IDN_VARIANT_2003, CONST_CS | CONST_PERSISTENT); +#ifdef HAVE_46_API + REGISTER_LONG_CONSTANT("INTL_IDNA_VARIANT_UTS46", INTL_IDN_VARIANT_UTS46, CONST_CS | CONST_PERSISTENT); +#endif + +#ifdef HAVE_46_API + /* PINFO ERROR CODES */ + REGISTER_LONG_CONSTANT("IDNA_ERROR_EMPTY_LABEL", UIDNA_ERROR_EMPTY_LABEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_LABEL_TOO_LONG", UIDNA_ERROR_LABEL_TOO_LONG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_DOMAIN_NAME_TOO_LONG", UIDNA_ERROR_DOMAIN_NAME_TOO_LONG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_LEADING_HYPHEN", UIDNA_ERROR_LEADING_HYPHEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_TRAILING_HYPHEN", UIDNA_ERROR_TRAILING_HYPHEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_HYPHEN_3_4", UIDNA_ERROR_HYPHEN_3_4, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_LEADING_COMBINING_MARK", UIDNA_ERROR_LEADING_COMBINING_MARK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_DISALLOWED", UIDNA_ERROR_DISALLOWED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_PUNYCODE", UIDNA_ERROR_PUNYCODE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_LABEL_HAS_DOT", UIDNA_ERROR_LABEL_HAS_DOT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_INVALID_ACE_LABEL", UIDNA_ERROR_INVALID_ACE_LABEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_BIDI", UIDNA_ERROR_BIDI, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IDNA_ERROR_CONTEXTJ", UIDNA_ERROR_CONTEXTJ, CONST_CS | CONST_PERSISTENT); +#endif } /* }}} */ @@ -54,11 +110,100 @@ enum { INTL_IDN_TO_UTF8 }; -static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS, int mode) +/* like INTL_CHECK_STATUS, but as a function and varying the name of the func */ +static int php_intl_idn_check_status(UErrorCode err, const char *msg, int mode TSRMLS_DC) +{ + intl_error_set_code(NULL, err TSRMLS_CC); + if (U_FAILURE(err)) { + char *buff; + spprintf(&buff, 0, "%s: %s", + mode == INTL_IDN_TO_ASCII ? "idn_to_ascii" : "idn_to_utf8", + msg); + intl_error_set_custom_msg(NULL, buff, 1 TSRMLS_CC); + efree(buff); + return FAILURE; + } + + return SUCCESS; +} + +static inline void php_intl_bad_args(const char *msg, int mode TSRMLS_DC) +{ + php_intl_idn_check_status(U_ILLEGAL_ARGUMENT_ERROR, msg, mode TSRMLS_CC); +} + +#ifdef HAVE_46_API +static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS, + const char *domain, int domain_len, uint32_t option, int mode, zval *idna_info) +{ + UErrorCode status = U_ZERO_ERROR; + UIDNA *uts46; + int32_t len; + int32_t buffer_capac = 255; /* no domain name may exceed this */ + char *buffer = emalloc(buffer_capac); + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + int buffer_used = 0; + + uts46 = uidna_openUTS46(option, &status); + if (php_intl_idn_check_status(status, "failed to open UIDNA instance", + mode TSRMLS_CC) == FAILURE) { + efree(buffer); + RETURN_FALSE; + } + + if (mode == INTL_IDN_TO_ASCII) { + len = uidna_nameToASCII_UTF8(uts46, domain, (int32_t)domain_len, + buffer, buffer_capac, &info, &status); + } else { + len = uidna_nameToUnicodeUTF8(uts46, domain, (int32_t)domain_len, + buffer, buffer_capac, &info, &status); + } + if (php_intl_idn_check_status(status, "failed to convert name", + mode TSRMLS_CC) == FAILURE) { + uidna_close(uts46); + efree(buffer); + RETURN_FALSE; + } + if (len >= 255) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "ICU returned an unexpected length"); + } + + buffer[len] = '\0'; + + if (info.errors == 0) { + RETVAL_STRINGL(buffer, len, 0); + buffer_used = 1; + } else { + RETVAL_FALSE; + } + + if (idna_info) { + if (buffer_used) { /* used in return_value then */ + zval_addref_p(return_value); + add_assoc_zval_ex(idna_info, "result", sizeof("result"), return_value); + } else { + zval *zv; + ALLOC_INIT_ZVAL(zv); + ZVAL_STRINGL(zv, buffer, len, 0); + buffer_used = 1; + add_assoc_zval_ex(idna_info, "result", sizeof("result"), zv); + } + add_assoc_bool_ex(idna_info, "isTransitionalDifferent", + sizeof("isTransitionalDifferent"), info.isTransitionalDifferent); + add_assoc_long_ex(idna_info, "errors", sizeof("errors"), (long)info.errors); + } + + if (!buffer_used) { + efree(buffer); + } + + uidna_close(uts46); +} +#endif + +static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS, + const char *domain, int domain_len, uint32_t option, int mode) { - unsigned char* domain; - int domain_len; - long option = 0; UChar* ustring = NULL; int ustring_len = 0; UErrorCode status; @@ -67,18 +212,9 @@ static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS, int mode) UChar converted[MAXPATHLEN]; int32_t converted_ret_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", (char **)&domain, &domain_len, &option) == FAILURE) { - return; - } - - if (domain_len < 1) { - intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "idn_to_ascii: empty domain name", 0 TSRMLS_CC ); - RETURN_FALSE; - } - /* convert the string to UTF-16. */ status = U_ZERO_ERROR; - intl_convert_utf8_to_utf16(&ustring, &ustring_len, (char*) domain, domain_len, &status ); + intl_convert_utf8_to_utf16(&ustring, &ustring_len, domain, domain_len, &status); if (U_FAILURE(status)) { intl_error_set_code(NULL, status TSRMLS_CC); @@ -123,20 +259,84 @@ static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS, int mode) RETURN_STRINGL(((char *)converted_utf8), converted_utf8_len, 0); } -/* {{{ proto int idn_to_ascii(string domain[, int options]) +static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + char *domain; + int domain_len; + long option = 0, + variant = INTL_IDN_VARIANT_2003; + zval *idna_info = NULL; + + intl_error_reset(NULL TSRMLS_CC); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|llz", + &domain, &domain_len, &option, &variant, &idna_info) == FAILURE) { + php_intl_bad_args("bad arguments", mode TSRMLS_CC); + RETURN_NULL(); /* don't set FALSE because that's not the way it was before... */ + } + +#ifdef HAVE_46_API + if (variant != INTL_IDN_VARIANT_2003 && variant != INTL_IDN_VARIANT_UTS46) { + php_intl_bad_args("invalid variant, must be one of {" + "INTL_IDNA_VARIANT_2003, INTL_IDNA_VARIANT_UTS46}", mode TSRMLS_CC); + RETURN_FALSE; + } +#else + if (variant != INTL_IDN_VARIANT_2003) { + php_intl_bad_args("invalid variant, PHP was compiled against " + "an old version of ICU and only supports INTL_IDN_VARIANT_2003", + mode TSRMLS_CC); + RETURN_FALSE; + } +#endif + + if (domain_len < 1) { + php_intl_bad_args("empty domain name", mode TSRMLS_CC); + RETURN_FALSE; + } + if (domain_len > INT32_MAX - 1) { + php_intl_bad_args("domain name too large", mode TSRMLS_CC); + RETURN_FALSE; + } + /* don't check options; it wasn't checked before */ + + if (idna_info != NULL) { + if (variant == INTL_IDN_VARIANT_2003) { + php_error_docref0(NULL TSRMLS_CC, E_NOTICE, + "4 arguments were provided, but INTL_IDNA_VARIANT_2003 only " + "takes 3 - extra argument ignored"); + } else { + zval_dtor(idna_info); + array_init(idna_info); + } + } + + if (variant == INTL_IDN_VARIANT_2003) { + php_intl_idn_to(INTERNAL_FUNCTION_PARAM_PASSTHRU, + domain, domain_len, (uint32_t)option, mode); + } +#ifdef HAVE_46_API + else { + php_intl_idn_to_46(INTERNAL_FUNCTION_PARAM_PASSTHRU, domain, domain_len, + (uint32_t)option, mode, idna_info); + } +#endif +} + +/* {{{ proto int idn_to_ascii(string domain[, int options[, int variant[, array &idna_info]]]) Converts an Unicode domain to ASCII representation, as defined in the IDNA RFC */ PHP_FUNCTION(idn_to_ascii) { - php_intl_idn_to(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_ASCII); + php_intl_idn_handoff(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_ASCII); } /* }}} */ -/* {{{ proto int idn_to_utf8(string domain[, int options]) +/* {{{ proto int idn_to_utf8(string domain[, int options[, int variant[, array &idna_info]]]) Converts an ASCII representation of the domain to Unicode (UTF-8), as defined in the IDNA RFC */ PHP_FUNCTION(idn_to_utf8) { - php_intl_idn_to(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_UTF8); + php_intl_idn_handoff(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_UTF8); } /* }}} */ diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c index 33f1730625..92cdc4cef4 100644 --- a/ext/intl/intl_convert.c +++ b/ext/intl/intl_convert.c @@ -67,7 +67,7 @@ void intl_convert_utf8_to_utf16( return; } - /* Bail out if an unexpected error occured. + /* Bail out if an unexpected error occurred. * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough). * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty). */ @@ -119,7 +119,7 @@ void intl_convert_utf16_to_utf8( *status = U_ZERO_ERROR; u_strToUTF8( NULL, 0, &dst_len, src, src_len, status ); - /* Bail out if an unexpected error occured. + /* Bail out if an unexpected error occurred. * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough). * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty). */ diff --git a/ext/intl/locale/locale_class.c b/ext/intl/locale/locale_class.c index d433bf5839..432cfb28fc 100644 --- a/ext/intl/locale/locale_class.c +++ b/ext/intl/locale/locale_class.c @@ -66,7 +66,7 @@ ZEND_END_ARG_INFO() * Every 'Locale' class method has an entry in this table */ -function_entry Locale_class_functions[] = { +zend_function_entry Locale_class_functions[] = { ZEND_FENTRY( getDefault, zif_locale_get_default , locale_0_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( setDefault, zif_locale_set_default , locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( getPrimaryLanguage, ZEND_FN( locale_get_primary_language ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c index 7ed28df3dc..8145a46f17 100644 --- a/ext/intl/msgformat/msgformat_class.c +++ b/ext/intl/msgformat/msgformat_class.c @@ -60,6 +60,7 @@ zend_object_value MessageFormatter_object_create(zend_class_entry *ce TSRMLS_DC) intern = ecalloc( 1, sizeof(MessageFormatter_object) ); msgformat_data_init( &intern->mf_data TSRMLS_CC ); zend_object_std_init( &intern->zo, ce TSRMLS_CC ); + object_properties_init(&intern->zo, ce); retval.handle = zend_objects_store_put( intern, @@ -132,7 +133,7 @@ ZEND_END_ARG_INFO() /* {{{ MessageFormatter_class_functions * Every 'MessageFormatter' class method has an entry in this table */ -static function_entry MessageFormatter_class_functions[] = { +static zend_function_entry MessageFormatter_class_functions[] = { PHP_ME( MessageFormatter, __construct, arginfo_messageformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) ZEND_FENTRY( create, ZEND_FN( msgfmt_create ), arginfo_messageformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) PHP_NAMED_FE( format, ZEND_FN( msgfmt_format ), arginfo_messageformatter_format ) diff --git a/ext/intl/normalizer/normalizer_class.c b/ext/intl/normalizer/normalizer_class.c index c5adf781ad..154d877e3f 100644 --- a/ext/intl/normalizer/normalizer_class.c +++ b/ext/intl/normalizer/normalizer_class.c @@ -41,7 +41,7 @@ ZEND_END_ARG_INFO() * Every 'Normalizer' class method has an entry in this table */ -function_entry Normalizer_class_functions[] = { +zend_function_entry Normalizer_class_functions[] = { ZEND_FENTRY( normalize, ZEND_FN( normalizer_normalize ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( isNormalized, ZEND_FN( normalizer_is_normalized ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) PHP_FE_END diff --git a/ext/intl/normalizer/normalizer_normalize.c b/ext/intl/normalizer/normalizer_normalize.c index 466ab97e12..f46285e9d9 100644 --- a/ext/intl/normalizer/normalizer_normalize.c +++ b/ext/intl/normalizer/normalizer_normalize.c @@ -110,7 +110,7 @@ PHP_FUNCTION( normalizer_normalize ) /* normalize */ size_needed = unorm_normalize( uinput, uinput_len, form, (int32_t) 0 /* options */, uret_buf, uret_len, &status); - /* Bail out if an unexpected error occured. + /* Bail out if an unexpected error occurred. * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough). * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty). */ @@ -133,7 +133,7 @@ PHP_FUNCTION( normalizer_normalize ) /* try normalize again */ size_needed = unorm_normalize( uinput, uinput_len, form, (int32_t) 0 /* options */, uret_buf, uret_len, &status); - /* Bail out if an unexpected error occured. */ + /* Bail out if an unexpected error occurred. */ if( U_FAILURE(status) ) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error normalizing string", 0 TSRMLS_CC ); @@ -234,7 +234,7 @@ PHP_FUNCTION( normalizer_is_normalized ) efree( uinput ); - /* Bail out if an unexpected error occured. */ + /* Bail out if an unexpected error occurred. */ if( U_FAILURE(status) ) { /* Set error messages. */ intl_error_set_custom_msg( NULL,"Error testing if string is the given normalization form.", 0 TSRMLS_CC ); diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index dc6c0fffa3..efe0ddd242 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -64,8 +64,19 @@ #include "resourcebundle/resourcebundle_class.h" +#include "transliterator/transliterator.h" +#include "transliterator/transliterator_class.h" +#include "transliterator/transliterator_methods.h" + #include "idn/idn.h" +#if U_ICU_VERSION_MAJOR_NUM > 3 && U_ICU_VERSION_MINOR_NUM >=2 +# include "spoofchecker/spoofchecker_class.h" +# include "spoofchecker/spoofchecker.h" +# include "spoofchecker/spoofchecker_create.h" +# include "spoofchecker/spoofchecker_main.h" +#endif + #include "msgformat/msgformat.h" #include "common/common_error.h" @@ -324,13 +335,15 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_ascii, 0, 0, 1) ZEND_ARG_INFO(0, domain) ZEND_ARG_INFO(0, option) - ZEND_ARG_INFO(0, status) + ZEND_ARG_INFO(0, variant) + ZEND_ARG_INFO(1, idn_info) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_utf8, 0, 0, 1) ZEND_ARG_INFO(0, domain) ZEND_ARG_INFO(0, option) - ZEND_ARG_INFO(0, status) + ZEND_ARG_INFO(0, variant) + ZEND_ARG_INFO(1, idn_info) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_create_proc, 0, 0, 2 ) @@ -361,6 +374,33 @@ ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_error_message_proc, 0, 0, 1 ) ZEND_ARG_INFO( 0, bundle ) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_void, 0, 0, 0 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create, 0, 0, 1 ) + ZEND_ARG_INFO( 0, id ) + ZEND_ARG_INFO( 0, direction ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create_from_rules, 0, 0, 1 ) + ZEND_ARG_INFO( 0, rules ) + ZEND_ARG_INFO( 0, direction ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create_inverse, 0, 0, 1 ) + ZEND_ARG_OBJ_INFO( 0, orig_trans, Transliterator, 0 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_transliterate, 0, 0, 2 ) + ZEND_ARG_INFO( 0, trans ) + ZEND_ARG_INFO( 0, subject ) + ZEND_ARG_INFO( 0, start ) + ZEND_ARG_INFO( 0, end ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_error, 0, 0, 1 ) + ZEND_ARG_OBJ_INFO( 0, trans, Transliterator, 0 ) +ZEND_END_ARG_INFO() /* }}} */ @@ -480,6 +520,15 @@ zend_function_entry intl_functions[] = { PHP_FE( resourcebundle_locales, arginfo_resourcebundle_locales_proc ) PHP_FE( resourcebundle_get_error_code, arginfo_resourcebundle_get_error_code_proc ) PHP_FE( resourcebundle_get_error_message, arginfo_resourcebundle_get_error_message_proc ) + + /* Transliterator functions */ + PHP_FE( transliterator_create, arginfo_transliterator_create ) + PHP_FE( transliterator_create_from_rules, arginfo_transliterator_create_from_rules ) + PHP_FE( transliterator_list_ids, arginfo_transliterator_void ) + PHP_FE( transliterator_create_inverse, arginfo_transliterator_create_inverse) + PHP_FE( transliterator_transliterate, arginfo_transliterator_transliterate ) + PHP_FE( transliterator_get_error_code, arginfo_transliterator_error ) + PHP_FE( transliterator_get_error_message, arginfo_transliterator_error ) /* common functions */ PHP_FE( intl_get_error_code, intl_0_args ) @@ -585,12 +634,25 @@ PHP_MINIT_FUNCTION( intl ) /* Register 'ResourceBundle' PHP class */ resourcebundle_register_class( TSRMLS_C); + /* Register 'Transliterator' PHP class */ + transliterator_register_Transliterator_class( TSRMLS_C ); + + /* Register Transliterator constants */ + transliterator_register_constants( INIT_FUNC_ARGS_PASSTHRU ); + /* Expose ICU error codes to PHP scripts. */ intl_expose_icu_error_codes( INIT_FUNC_ARGS_PASSTHRU ); /* Expose IDN constants to PHP scripts. */ idn_register_constants(INIT_FUNC_ARGS_PASSTHRU); +#if U_ICU_VERSION_MAJOR_NUM > 3 && U_ICU_VERSION_MINOR_NUM >=2 + /* Register 'Spoofchecker' PHP class */ + spoofchecker_register_Spoofchecker_class( TSRMLS_C ); + + /* Expose Spoofchecker constants to PHP scripts */ + spoofchecker_register_constants( INIT_FUNC_ARGS_PASSTHRU ); +#endif /* Global error handling. */ intl_error_init( NULL TSRMLS_CC ); diff --git a/ext/intl/resourcebundle/resourcebundle.c b/ext/intl/resourcebundle/resourcebundle.c index 237d6c8d1e..6d39dfb7e0 100644 --- a/ext/intl/resourcebundle/resourcebundle.c +++ b/ext/intl/resourcebundle/resourcebundle.c @@ -1,4 +1,4 @@ - /* +/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ diff --git a/ext/intl/resourcebundle/resourcebundle_class.c b/ext/intl/resourcebundle/resourcebundle_class.c index d2a29d9b25..23e9449a38 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.c +++ b/ext/intl/resourcebundle/resourcebundle_class.c @@ -63,6 +63,7 @@ static zend_object_value ResourceBundle_object_create( zend_class_entry *ce TSRM rb = ecalloc( 1, sizeof(ResourceBundle_object) ); zend_object_std_init( (zend_object *) rb, ce TSRMLS_CC ); + object_properties_init((zend_object *) rb, ce); intl_error_init( INTL_DATA_ERROR_P(rb) TSRMLS_CC ); rb->me = NULL; @@ -91,7 +92,7 @@ static void resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) intl_error_reset( NULL TSRMLS_CC ); - if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", + if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s!s!|b", &locale, &locale_len, &bundlename, &bundlename_len, &fallback ) == FAILURE ) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, @@ -101,6 +102,10 @@ static void resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) } INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value); + + if (locale == NULL) { + locale = INTL_G(default_locale); + } if (fallback) { rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); @@ -110,13 +115,17 @@ static void resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) INTL_CTOR_CHECK_STATUS(rb, "resourcebundle_ctor: Cannot load libICU resource bundle"); - if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) { - intl_errors_set_code( NULL, INTL_DATA_ERROR_CODE(rb) TSRMLS_CC ); - spprintf( &pbuf, 0, "resourcebundle_ctor: Cannot load libICU resource '%s' without fallback from %s to %s", - bundlename, locale, ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(rb)) ); - intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 TSRMLS_CC ); + if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || + INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) { + intl_errors_set_code(NULL, INTL_DATA_ERROR_CODE(rb) TSRMLS_CC); + spprintf(&pbuf, 0, "resourcebundle_ctor: Cannot load libICU resource " + "'%s' without fallback from %s to %s", + bundlename ? bundlename : "(default data)", locale, + ures_getLocaleByType( + rb->me, ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(rb))); + intl_errors_set_custom_msg(INTL_DATA_ERROR_P(rb), pbuf, 1 TSRMLS_CC); efree(pbuf); - zval_dtor( return_value ); + zval_dtor(return_value); RETURN_NULL(); } } @@ -391,7 +400,7 @@ PHP_FUNCTION( resourcebundle_get_error_message ) /* {{{ ResourceBundle_class_functions * Every 'ResourceBundle' class method has an entry in this table */ -static function_entry ResourceBundle_class_functions[] = { +static zend_function_entry ResourceBundle_class_functions[] = { PHP_ME( ResourceBundle, __construct, arginfo_resourcebundle___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) ZEND_NAMED_ME( create, ZEND_FN( resourcebundle_create ), arginfo_resourcebundle___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_NAMED_ME( get, ZEND_FN(resourcebundle_get), arginfo_resourcebundle_get, ZEND_ACC_PUBLIC ) diff --git a/ext/intl/spoofchecker/spoofchecker.c b/ext/intl/spoofchecker/spoofchecker.c new file mode 100755 index 0000000000..42a014a90e --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker.c @@ -0,0 +1,60 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "spoofchecker_class.h" +#include "spoofchecker.h" + +#include <unicode/uspoof.h> + + +/* {{{ spoofchecker_register_constants + * Register constants + */ +void spoofchecker_register_constants(INIT_FUNC_ARGS) +{ + if (!Spoofchecker_ce_ptr) + { + zend_error(E_ERROR, "Spoofchecker class not defined"); + return; + } + + #define SPOOFCHECKER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long(Spoofchecker_ce_ptr, ZEND_STRS( #x ) - 1, USPOOF_##x TSRMLS_CC); + + SPOOFCHECKER_EXPOSE_CLASS_CONST(SINGLE_SCRIPT_CONFUSABLE) + SPOOFCHECKER_EXPOSE_CLASS_CONST(MIXED_SCRIPT_CONFUSABLE) + SPOOFCHECKER_EXPOSE_CLASS_CONST(WHOLE_SCRIPT_CONFUSABLE) + SPOOFCHECKER_EXPOSE_CLASS_CONST(ANY_CASE) + SPOOFCHECKER_EXPOSE_CLASS_CONST(SINGLE_SCRIPT) + SPOOFCHECKER_EXPOSE_CLASS_CONST(INVISIBLE) + SPOOFCHECKER_EXPOSE_CLASS_CONST(CHAR_LIMIT) + + + #undef SPOOFCHECKER_EXPOSE_CLASS_CONST +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/spoofchecker/spoofchecker.h b/ext/intl/spoofchecker/spoofchecker.h new file mode 100755 index 0000000000..f976d639ac --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker.h @@ -0,0 +1,24 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef SPOOFCHECKER_SPOOFCHECKER_H +#define SPOOFCHECKER_SPOOFCHECKER_H + +#include <php.h> + +void spoofchecker_register_constants(INIT_FUNC_ARGS); + +#endif // SPOOFCHECKER_SPOOFCHECKER_H diff --git a/ext/intl/spoofchecker/spoofchecker_class.c b/ext/intl/spoofchecker/spoofchecker_class.c new file mode 100755 index 0000000000..507a2ca98e --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_class.c @@ -0,0 +1,210 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#include "spoofchecker_class.h" +#include "spoofchecker_main.h" +#include "spoofchecker_create.h" +#include "php_intl.h" +#include "intl_error.h" + +#include <unicode/uspoof.h> + +zend_class_entry *Spoofchecker_ce_ptr = NULL; +static zend_object_handlers Spoofchecker_handlers; + +/* + * Auxiliary functions needed by objects of 'Spoofchecker' class + */ + +/* {{{ Spoofchecker_objects_dtor */ +static void Spoofchecker_objects_dtor( + void *object, + zend_object_handle handle TSRMLS_DC) +{ + zend_objects_destroy_object(object, handle TSRMLS_CC); +} +/* }}} */ + +/* {{{ Spoofchecker_objects_free */ +void Spoofchecker_objects_free(zend_object *object TSRMLS_DC) +{ + Spoofchecker_object* co = (Spoofchecker_object*)object; + + zend_object_std_dtor(&co->zo TSRMLS_CC); + + spoofchecker_object_destroy(co TSRMLS_CC); + + efree(co); +} +/* }}} */ + +/* {{{ Spoofchecker_object_create */ +zend_object_value Spoofchecker_object_create( + zend_class_entry *ce TSRMLS_DC) +{ + zend_object_value retval; + Spoofchecker_object* intern; + + intern = ecalloc(1, sizeof(Spoofchecker_object)); + intl_error_init(SPOOFCHECKER_ERROR_P(intern) TSRMLS_CC); + zend_object_std_init(&intern->zo, ce TSRMLS_CC); + object_properties_init(&intern->zo, ce); + + retval.handle = zend_objects_store_put( + intern, + Spoofchecker_objects_dtor, + (zend_objects_free_object_storage_t)Spoofchecker_objects_free, + NULL TSRMLS_CC); + + retval.handlers = &Spoofchecker_handlers; + + return retval; +} +/* }}} */ + +/* + * 'Spoofchecker' class registration structures & functions + */ + +/* {{{ Spoofchecker methods arguments info */ +ZEND_BEGIN_ARG_INFO_EX(spoofchecker_0_args, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(spoofchecker_set_checks, 0, 0, 1) + ZEND_ARG_INFO(0, checks) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(spoofchecker_set_allowed_locales, 0, 0, 1) + ZEND_ARG_INFO(0, locale_list) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(spoofchecker_is_suspicous, 0, 0, 1) + ZEND_ARG_INFO(0, text) + ZEND_ARG_INFO(1, error) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(spoofchecker_are_confusable, 0, 0, 2) + ZEND_ARG_INFO(0, s1) + ZEND_ARG_INFO(0, s2) + ZEND_ARG_INFO(1, error) +ZEND_END_ARG_INFO() + +/* }}} */ + +/* {{{ Spoofchecker_class_functions + * Every 'Spoofchecker' class method has an entry in this table + */ + +zend_function_entry Spoofchecker_class_functions[] = { + PHP_ME(Spoofchecker, __construct, spoofchecker_0_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) + PHP_ME(Spoofchecker, isSuspicious, spoofchecker_is_suspicous, ZEND_ACC_PUBLIC) + PHP_ME(Spoofchecker, areConfusable, spoofchecker_are_confusable, ZEND_ACC_PUBLIC) + PHP_ME(Spoofchecker, setAllowedLocales, spoofchecker_set_allowed_locales, ZEND_ACC_PUBLIC) + PHP_ME(Spoofchecker, setChecks, spoofchecker_set_checks, ZEND_ACC_PUBLIC) + PHP_FE_END +}; +/* }}} */ + +static zend_object_value spoofchecker_clone_obj(zval *object TSRMLS_DC) /* {{{ */ +{ + zend_object_value new_obj_val; + zend_object_handle handle = Z_OBJ_HANDLE_P(object); + Spoofchecker_object *sfo, *new_sfo; + + sfo = (Spoofchecker_object *) zend_object_store_get_object(object TSRMLS_CC); + intl_error_reset(SPOOFCHECKER_ERROR_P(sfo) TSRMLS_CC); + + new_obj_val = Spoofchecker_ce_ptr->create_object(Spoofchecker_ce_ptr TSRMLS_CC); + new_sfo = (Spoofchecker_object *)zend_object_store_get_object_by_handle(new_obj_val.handle TSRMLS_CC); + /* clone standard parts */ + zend_objects_clone_members(&new_sfo->zo, new_obj_val, &sfo->zo, handle TSRMLS_CC); + /* clone internal object */ + new_sfo->uspoof = uspoof_clone(sfo->uspoof, SPOOFCHECKER_ERROR_CODE_P(new_sfo)); + if(U_FAILURE(SPOOFCHECKER_ERROR_CODE(new_sfo))) { + /* set up error in case error handler is interested */ + intl_error_set( NULL, SPOOFCHECKER_ERROR_CODE(new_sfo), "Failed to clone SpoofChecker object", 0 TSRMLS_CC ); + Spoofchecker_objects_dtor(new_sfo, new_obj_val.handle TSRMLS_CC); /* free new object */ + zend_error(E_ERROR, "Failed to clone SpoofChecker object"); + } + return new_obj_val; +} +/* }}} */ + +/* {{{ spoofchecker_register_Spoofchecker_class + * Initialize 'Spoofchecker' class + */ +void spoofchecker_register_Spoofchecker_class(TSRMLS_D) +{ + zend_class_entry ce; + + /* Create and register 'Spoofchecker' class. */ + INIT_CLASS_ENTRY(ce, "Spoofchecker", Spoofchecker_class_functions); + ce.create_object = Spoofchecker_object_create; + Spoofchecker_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC); + + memcpy(&Spoofchecker_handlers, zend_get_std_object_handlers(), + sizeof Spoofchecker_handlers); + Spoofchecker_handlers.clone_obj = spoofchecker_clone_obj; + + if (!Spoofchecker_ce_ptr) { + zend_error(E_ERROR, + "Spoofchecker: attempt to create properties " + "on a non-registered class."); + return; + } +} +/* }}} */ + +/* {{{ void spoofchecker_object_init( Spoofchecker_object* co ) + * Initialize internals of Spoofchecker_object. + * Must be called before any other call to 'spoofchecker_object_...' functions. + */ +void spoofchecker_object_init(Spoofchecker_object* co TSRMLS_DC) +{ + if (!co) { + return; + } + + intl_error_init(SPOOFCHECKER_ERROR_P(co) TSRMLS_CC); +} +/* }}} */ + +/* {{{ void spoofchecker_object_destroy( Spoofchecker_object* co ) + * Clean up mem allocted by internals of Spoofchecker_object + */ +void spoofchecker_object_destroy(Spoofchecker_object* co TSRMLS_DC) +{ + if (!co) { + return; + } + + if (co->uspoof) { + uspoof_close(co->uspoof); + co->uspoof = NULL; + } + + intl_error_reset(SPOOFCHECKER_ERROR_P(co) TSRMLS_CC); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/spoofchecker/spoofchecker_class.h b/ext/intl/spoofchecker/spoofchecker_class.h new file mode 100755 index 0000000000..8db64680ef --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_class.h @@ -0,0 +1,70 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef SPOOFCHECKER_CLASS_H +#define SPOOFCHECKER_CLASS_H + +#include <php.h> + +#include "intl_common.h" +#include "spoofchecker_create.h" +#include "intl_error.h" + +#include <unicode/uspoof.h> + +typedef struct { + zend_object zo; + + // error handling + intl_error err; + + // ICU Spoofchecker + USpoofChecker* uspoof; +} Spoofchecker_object; + +#define SPOOFCHECKER_ERROR(co) (co)->err +#define SPOOFCHECKER_ERROR_P(co) &(SPOOFCHECKER_ERROR(co)) + +#define SPOOFCHECKER_ERROR_CODE(co) INTL_ERROR_CODE(SPOOFCHECKER_ERROR(co)) +#define SPOOFCHECKER_ERROR_CODE_P(co) &(INTL_ERROR_CODE(SPOOFCHECKER_ERROR(co))) + +void spoofchecker_register_Spoofchecker_class(TSRMLS_D); + +void spoofchecker_object_init(Spoofchecker_object* co TSRMLS_DC); +void spoofchecker_object_destroy(Spoofchecker_object* co TSRMLS_DC); + +extern zend_class_entry *Spoofchecker_ce_ptr; + +/* Auxiliary macros */ + +#define SPOOFCHECKER_METHOD_INIT_VARS \ + zval* object = getThis(); \ + Spoofchecker_object* co = NULL; \ + intl_error_reset(NULL TSRMLS_CC); \ + +#define SPOOFCHECKER_METHOD_FETCH_OBJECT \ + co = (Spoofchecker_object *) zend_object_store_get_object(object TSRMLS_CC); \ + intl_error_reset(SPOOFCHECKER_ERROR_P(co) TSRMLS_CC); \ + +// Macro to check return value of a ucol_* function call. +#define SPOOFCHECKER_CHECK_STATUS(co, msg) \ + intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co) TSRMLS_CC); \ + if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { \ + intl_errors_set_custom_msg(SPOOFCHECKER_ERROR_P(co), msg, 0 TSRMLS_CC); \ + RETURN_FALSE; \ + } \ + +#endif // #ifndef SPOOFCHECKER_CLASS_H diff --git a/ext/intl/spoofchecker/spoofchecker_create.c b/ext/intl/spoofchecker/spoofchecker_create.c new file mode 100755 index 0000000000..3659551ede --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_create.c @@ -0,0 +1,59 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "spoofchecker_class.h" +#include "spoofchecker_create.h" +#include "intl_data.h" + +/* {{{ proto Spoofchecker Spoofchecker::__construct() + * Spoofchecker object constructor. + */ +PHP_METHOD(Spoofchecker, __construct) +{ + int checks; + SPOOFCHECKER_METHOD_INIT_VARS; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + SPOOFCHECKER_METHOD_FETCH_OBJECT; + + co->uspoof = uspoof_open(SPOOFCHECKER_ERROR_CODE_P(co)); + INTL_CTOR_CHECK_STATUS(co, "spoofchecker: unable to open ICU Spoof Checker"); + + /* Single-script enforcement is on by default. This fails for languages + like Japanese that legally use multiple scripts within a single word, + so we turn it off. + */ + checks = uspoof_getChecks(co->uspoof, SPOOFCHECKER_ERROR_CODE_P(co)); + uspoof_setChecks(co->uspoof, checks & ~USPOOF_SINGLE_SCRIPT, SPOOFCHECKER_ERROR_CODE_P(co)); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/spoofchecker/spoofchecker_create.h b/ext/intl/spoofchecker/spoofchecker_create.h new file mode 100755 index 0000000000..313faab8a3 --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_create.h @@ -0,0 +1,24 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef SPOOFCHECKER_CREATE_H +#define SPOOFCHECKER_CREATE_H + +#include <php.h> + +PHP_METHOD(Spoofchecker, __construct); + +#endif // SPOOFCHECKER_CREATE_H diff --git a/ext/intl/spoofchecker/spoofchecker_main.c b/ext/intl/spoofchecker/spoofchecker_main.c new file mode 100755 index 0000000000..c37b9186a2 --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_main.c @@ -0,0 +1,142 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "spoofchecker_class.h" + +/* {{{ proto bool Spoofchecker::isSuspicious( string text[, int &error_code ] ) + * Checks if a given text contains any suspicious characters + */ +PHP_METHOD(Spoofchecker, isSuspicious) +{ + int ret; + char *text; + int text_len; + zval *error_code = NULL; + SPOOFCHECKER_METHOD_INIT_VARS; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &text, &text_len, &error_code)) { + return; + } + + SPOOFCHECKER_METHOD_FETCH_OBJECT; + + ret = uspoof_checkUTF8(co->uspoof, text, text_len, NULL, SPOOFCHECKER_ERROR_CODE_P(co)); + + if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co))); + RETURN_TRUE; + } + + if (error_code) { + zval_dtor(error_code); + ZVAL_LONG(error_code, ret); + } + RETVAL_BOOL(ret != 0); +} +/* }}} */ + +/* {{{ proto bool Spoofchecker::areConfusable( string str1, string str2[, int &error_code ] ) + * Checks if a given text contains any confusable characters + */ +PHP_METHOD(Spoofchecker, areConfusable) +{ + int ret; + char *s1, *s2; + int s1_len, s2_len; + zval *error_code = NULL; + SPOOFCHECKER_METHOD_INIT_VARS; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|z", &s1, &s1_len, + &s2, &s2_len, &error_code)) { + return; + } + + SPOOFCHECKER_METHOD_FETCH_OBJECT; + + ret = uspoof_areConfusableUTF8(co->uspoof, s1, s1_len, s2, s2_len, SPOOFCHECKER_ERROR_CODE_P(co)); + + if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co))); + RETURN_TRUE; + } + + if (error_code) { + zval_dtor(error_code); + ZVAL_LONG(error_code, ret); + } + RETVAL_BOOL(ret != 0); +} +/* }}} */ + +/* {{{ proto void Spoofchecker::setAllowedLocales( string locales ) + * Locales to use when running checks + */ +PHP_METHOD(Spoofchecker, setAllowedLocales) +{ + char *locales; + int locales_len; + SPOOFCHECKER_METHOD_INIT_VARS; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &locales, &locales_len)) { + return; + } + + SPOOFCHECKER_METHOD_FETCH_OBJECT; + + uspoof_setAllowedLocales(co->uspoof, locales, SPOOFCHECKER_ERROR_CODE_P(co)); + + if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co))); + return; + } +} +/* }}} */ + +/* {{{ proto void Spoofchecker::setChecks( int checks ) + * Set the checks to run + */ +PHP_METHOD(Spoofchecker, setChecks) +{ + long checks; + SPOOFCHECKER_METHOD_INIT_VARS; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &checks)) { + return; + } + + SPOOFCHECKER_METHOD_FETCH_OBJECT; + + uspoof_setChecks(co->uspoof, checks, SPOOFCHECKER_ERROR_CODE_P(co)); + + if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co))); + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/spoofchecker/spoofchecker_main.h b/ext/intl/spoofchecker/spoofchecker_main.h new file mode 100755 index 0000000000..fb920d7841 --- /dev/null +++ b/ext/intl/spoofchecker/spoofchecker_main.h @@ -0,0 +1,27 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Scott MacVicar <scottmac@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef SPOOFCHECKER_MAIN_H +#define SPOOFCHECKER_MAIN_H + +#include <php.h> + +PHP_METHOD(Spoofchecker, isSuspicious); +PHP_METHOD(Spoofchecker, areConfusable); +PHP_METHOD(Spoofchecker, setAllowedLocales); +PHP_METHOD(Spoofchecker, setChecks); + +#endif // SPOOFCHECKER_MAIN_H diff --git a/ext/intl/tests/bug55562.phpt b/ext/intl/tests/bug55562.phpt new file mode 100644 index 0000000000..dd053c0ee9 --- /dev/null +++ b/ext/intl/tests/bug55562.phpt @@ -0,0 +1,13 @@ +--TEST-- +grapheme_substr() - Bug55562 - grapheme_substr() returns false if length parameter is to large +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +var_dump( + grapheme_substr('FOK', 1, 20), // expected: OK + grapheme_substr('한국어', 1, 20) //expected: 국어 +); +--EXPECT-- +string(2) "OK" +string(6) "국어" diff --git a/ext/intl/tests/bug59597_64.phpt b/ext/intl/tests/bug59597_64.phpt index 4b96bf72e9..f96c72da54 100644 --- a/ext/intl/tests/bug59597_64.phpt +++ b/ext/intl/tests/bug59597_64.phpt @@ -15,7 +15,7 @@ $value = $formatter->parse('2147483650', \NumberFormatter::TYPE_INT64); var_dump($value); ?> ---EXPECTREGEX-- +--EXPECT-- int(2147483647) int(2147483650) diff --git a/ext/intl/tests/formatter_get_error.phpt b/ext/intl/tests/formatter_get_error.phpt index c7b3972709..acb683e894 100644 --- a/ext/intl/tests/formatter_get_error.phpt +++ b/ext/intl/tests/formatter_get_error.phpt @@ -19,7 +19,7 @@ function ut_main() if( $num === false ) return $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n"; else - return "Ooops, an error should have occured."; + return "Ooops, an error should have occurred."; } include_once( 'ut_common.inc' ); diff --git a/ext/intl/tests/idn_uts46_basic.phpt b/ext/intl/tests/idn_uts46_basic.phpt new file mode 100644 index 0000000000..2ca185092d --- /dev/null +++ b/ext/intl/tests/idn_uts46_basic.phpt @@ -0,0 +1,53 @@ +--TEST-- +IDN UTS #46 API basic tests +--SKIPIF-- +<?php + if (!extension_loaded('intl')) + die('skip'); + if (!defined('INTL_IDNA_VARIANT_UTS46')) + die('skip no UTS #46 API'); +--FILE-- +<?php +$utf8dn = "www.fußball.com"; +$asciiNonTrans = "www.xn--fuball-cta.com"; + +echo "all ok, no details:", "\n"; +var_dump(idn_to_ascii($utf8dn, + IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46)); + +echo "all ok, no details, transitional:", "\n"; +var_dump(idn_to_ascii($utf8dn, 0, INTL_IDNA_VARIANT_UTS46)); + +echo "all ok, with details:", "\n"; +var_dump(idn_to_ascii($utf8dn, IDNA_NONTRANSITIONAL_TO_ASCII, + INTL_IDNA_VARIANT_UTS46, $info)); +var_dump($info); + +echo "reverse, ok, with details:", "\n"; +var_dump(idn_to_utf8($asciiNonTrans, 0, INTL_IDNA_VARIANT_UTS46, $info)); +var_dump($info); +--EXPECT-- +all ok, no details: +string(22) "www.xn--fuball-cta.com" +all ok, no details, transitional: +string(16) "www.fussball.com" +all ok, with details: +string(22) "www.xn--fuball-cta.com" +array(3) { + ["result"]=> + string(22) "www.xn--fuball-cta.com" + ["isTransitionalDifferent"]=> + bool(true) + ["errors"]=> + int(0) +} +reverse, ok, with details: +string(16) "www.fußball.com" +array(3) { + ["result"]=> + string(16) "www.fußball.com" + ["isTransitionalDifferent"]=> + bool(false) + ["errors"]=> + int(0) +} diff --git a/ext/intl/tests/idn_uts46_errors.phpt b/ext/intl/tests/idn_uts46_errors.phpt new file mode 100644 index 0000000000..a336e698c4 --- /dev/null +++ b/ext/intl/tests/idn_uts46_errors.phpt @@ -0,0 +1,89 @@ +--TEST-- +IDN UTS #46 API error tests +--SKIPIF-- +<?php + if (!extension_loaded('intl')) + die('skip'); + if (!defined('INTL_IDNA_VARIANT_UTS46')) + die('skip no UTS #46 API'); +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +echo "=> PHP level errors", "\n"; + +echo "bad args:", "\n"; +var_dump(idn_to_ascii("", 0, array())); +var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46, $foo, null)); + +echo "bad variant:", "\n"; +var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46 + 10)); + +echo "empty domain:", "\n"; +var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46)); + +echo "fourth arg for 2003 variant (only notice raised):", "\n"; +var_dump(idn_to_ascii("foo.com", 0, INTL_IDNA_VARIANT_2003, $foo)); + +echo "with error, but no details arg:", "\n"; +var_dump(idn_to_ascii("www.fußball.com-", 0, INTL_IDNA_VARIANT_UTS46)); + +echo "with error, with details arg:", "\n"; +var_dump(idn_to_ascii("www.fußball.com-", IDNA_NONTRANSITIONAL_TO_ASCII, + INTL_IDNA_VARIANT_UTS46, $foo)); +var_dump($foo); + +echo "with error, with details arg, contextj:", "\n"; +var_dump(idn_to_ascii( + html_entity_decode("www.a‍b.com", 0, "UTF-8"), + IDNA_NONTRANSITIONAL_TO_ASCII | IDNA_CHECK_CONTEXTJ, + INTL_IDNA_VARIANT_UTS46, $foo)); +var_dump($foo); +var_dump($foo["errors"]==IDNA_ERROR_CONTEXTJ); +--EXPECTF-- +=> PHP level errors +bad args: + +Warning: idn_to_ascii() expects parameter 3 to be long, array given in %s on line %d + +Warning: idn_to_ascii(): idn_to_ascii: bad arguments in %s on line %d +NULL + +Warning: idn_to_ascii() expects at most 4 parameters, 5 given in %s on line %d + +Warning: idn_to_ascii(): idn_to_ascii: bad arguments in %s on line %d +NULL +bad variant: + +Warning: idn_to_ascii(): idn_to_ascii: invalid variant, must be one of {INTL_IDNA_VARIANT_2003, INTL_IDNA_VARIANT_UTS46} in %s on line %d +bool(false) +empty domain: + +Warning: idn_to_ascii(): idn_to_ascii: empty domain name in %s on line %d +bool(false) +fourth arg for 2003 variant (only notice raised): + +Notice: idn_to_ascii(): 4 arguments were provided, but INTL_IDNA_VARIANT_2003 only takes 3 - extra argument ignored in %s on line %d +string(7) "foo.com" +with error, but no details arg: +bool(false) +with error, with details arg: +bool(false) +array(3) { + ["result"]=> + string(23) "www.xn--fuball-cta.com-" + ["isTransitionalDifferent"]=> + bool(true) + ["errors"]=> + int(16) +} +with error, with details arg, contextj: +bool(false) +array(3) { + ["result"]=> + string(18) "www.xn--ab-m1t.com" + ["isTransitionalDifferent"]=> + bool(true) + ["errors"]=> + int(4096) +} +bool(true) diff --git a/ext/intl/tests/locale_accept.phpt b/ext/intl/tests/locale_accept.phpt index 923571f8f0..cf1dc6e675 100644 --- a/ext/intl/tests/locale_accept.phpt +++ b/ext/intl/tests/locale_accept.phpt @@ -24,7 +24,7 @@ function ut_main() foreach($http_acc as $http) { $res = ut_loc_accept_http($http); - $res_str .= "Accepting $http: $res\n"; + $res_str .= @"Accepting $http: $res\n"; } return $res_str; diff --git a/ext/intl/tests/locale_get_display_script2.phpt b/ext/intl/tests/locale_get_display_script2.phpt index 92652bde90..2b9e037b78 100644 --- a/ext/intl/tests/locale_get_display_script2.phpt +++ b/ext/intl/tests/locale_get_display_script2.phpt @@ -1,8 +1,8 @@ --TEST-- -locale_get_display_script() icu >= 4.8 +locale_get_display_script() icu = 4.8 --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> -<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?> +<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0 || version_compare(INTL_ICU_VERSION, '49') >= 0) print 'skip'; ?> --FILE-- <?php diff --git a/ext/intl/tests/locale_get_display_script3.phpt b/ext/intl/tests/locale_get_display_script3.phpt new file mode 100644 index 0000000000..447766e6bd --- /dev/null +++ b/ext/intl/tests/locale_get_display_script3.phpt @@ -0,0 +1,275 @@ +--TEST-- +locale_get_display_script() icu >= 49 +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if(version_compare(INTL_ICU_VERSION, '49') < 0) print 'skip'; ?> +--FILE-- +<?php + +/* + * Try getting the display_script for different locales + * with Procedural and Object methods. + */ + +function ut_main() +{ + $res_str = ''; + + $disp_locales=array('en','fr','de'); + + $locales = array( + 'uk-ua_CALIFORNIA@currency=;currency=GRN', + 'root', + 'uk@currency=EURO', + 'Hindi', +//Simple language subtag + 'de', + 'fr', + 'ja', + 'i-enochian', //(example of a grandfathered tag) +//Language subtag plus Script subtag: + 'zh-Hant', + 'zh-Hans', + 'sr-Cyrl', + 'sr-Latn', +//Language-Script-Region + 'zh-Hans-CN', + 'sr-Latn-CS', +//Language-Variant + 'sl-rozaj', + 'sl-nedis', +//Language-Region-Variant + 'de-CH-1901', + 'sl-IT-nedis', +//Language-Script-Region-Variant + 'sl-Latn-IT-nedis', +//Language-Region: + 'de-DE', + 'en-US', + 'es-419', +//Private use subtags: + 'de-CH-x-phonebk', + 'az-Arab-x-AZE-derbend', +//Extended language subtags + 'zh-min', + 'zh-min-nan-Hant-CN', +//Private use registry values + 'x-whatever', + 'qaa-Qaaa-QM-x-southern', + 'sr-Latn-QM', + 'sr-Qaaa-CS', +/*Tags that use extensions (examples ONLY: extensions MUST be defined + by revision or update to this document or by RFC): */ + 'en-US-u-islamCal', + 'zh-CN-a-myExt-x-private', + 'en-a-myExt-b-another', +//Some Invalid Tags: + 'de-419-DE', + 'a-DE', + 'ar-a-aaa-b-bbb-a-ccc' + ); + + + $res_str = ''; + + foreach( $locales as $locale ) + { + $res_str .= "locale='$locale'\n"; + foreach( $disp_locales as $disp_locale ) + { + $scr = ut_loc_get_display_script( $locale ,$disp_locale ); + $res_str .= "disp_locale=$disp_locale : display_script=$scr"; + $res_str .= "\n"; + } + $res_str .= "-----------------\n"; + } + + return $res_str; + +} + +include_once( 'ut_common.inc' ); +ut_run(); + +?> +--EXPECT-- +locale='uk-ua_CALIFORNIA@currency=;currency=GRN' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='root' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='uk@currency=EURO' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='Hindi' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='de' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='fr' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='ja' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='i-enochian' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='zh-Hant' +disp_locale=en : display_script=Traditional Han +disp_locale=fr : display_script=chinois traditionnel +disp_locale=de : display_script=Traditionelles Chinesisch +----------------- +locale='zh-Hans' +disp_locale=en : display_script=Simplified Han +disp_locale=fr : display_script=chinois simplifié +disp_locale=de : display_script=Vereinfachtes Chinesisch +----------------- +locale='sr-Cyrl' +disp_locale=en : display_script=Cyrillic +disp_locale=fr : display_script=cyrillique +disp_locale=de : display_script=Kyrillisch +----------------- +locale='sr-Latn' +disp_locale=en : display_script=Latin +disp_locale=fr : display_script=latin +disp_locale=de : display_script=Lateinisch +----------------- +locale='zh-Hans-CN' +disp_locale=en : display_script=Simplified Han +disp_locale=fr : display_script=chinois simplifié +disp_locale=de : display_script=Vereinfachtes Chinesisch +----------------- +locale='sr-Latn-CS' +disp_locale=en : display_script=Latin +disp_locale=fr : display_script=latin +disp_locale=de : display_script=Lateinisch +----------------- +locale='sl-rozaj' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='sl-nedis' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='de-CH-1901' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='sl-IT-nedis' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='sl-Latn-IT-nedis' +disp_locale=en : display_script=Latin +disp_locale=fr : display_script=latin +disp_locale=de : display_script=Lateinisch +----------------- +locale='de-DE' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='en-US' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='es-419' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='de-CH-x-phonebk' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='az-Arab-x-AZE-derbend' +disp_locale=en : display_script=Arabic +disp_locale=fr : display_script=arabe +disp_locale=de : display_script=Arabisch +----------------- +locale='zh-min' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='zh-min-nan-Hant-CN' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='x-whatever' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='qaa-Qaaa-QM-x-southern' +disp_locale=en : display_script=Qaaa +disp_locale=fr : display_script=Qaaa +disp_locale=de : display_script=Qaaa +----------------- +locale='sr-Latn-QM' +disp_locale=en : display_script=Latin +disp_locale=fr : display_script=latin +disp_locale=de : display_script=Lateinisch +----------------- +locale='sr-Qaaa-CS' +disp_locale=en : display_script=Qaaa +disp_locale=fr : display_script=Qaaa +disp_locale=de : display_script=Qaaa +----------------- +locale='en-US-u-islamCal' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='zh-CN-a-myExt-x-private' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='en-a-myExt-b-another' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='de-419-DE' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='a-DE' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- +locale='ar-a-aaa-b-bbb-a-ccc' +disp_locale=en : display_script= +disp_locale=fr : display_script= +disp_locale=de : display_script= +----------------- diff --git a/ext/intl/tests/resourcebundle_null_mandatory_args.phpt b/ext/intl/tests/resourcebundle_null_mandatory_args.phpt new file mode 100644 index 0000000000..8fde61bd21 --- /dev/null +++ b/ext/intl/tests/resourcebundle_null_mandatory_args.phpt @@ -0,0 +1,26 @@ +--TEST-- +IntlCalendar::setTime() basic test +--INI-- +date.timezone=Atlantic/Azores +--SKIPIF-- +<?php +if (!extension_loaded('intl')) + die('skip intl extension not enabled'); +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); + +$r = new ResourceBundle('en_US', NULL); +$c = $r->get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0); +var_dump($c); + +ini_set('intl.default_locale', 'pt_PT'); +$r = new ResourceBundle(NULL, NULL); +$c = $r->get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0); +var_dump($c); +?> +==DONE== +--EXPECT-- +string(14) "h:mm:ss a zzzz" +string(12) "H:mm:ss zzzz" +==DONE== diff --git a/ext/intl/tests/spoofchecker_001.phpt b/ext/intl/tests/spoofchecker_001.phpt new file mode 100755 index 0000000000..7904b3a4c0 --- /dev/null +++ b/ext/intl/tests/spoofchecker_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +spoofchecker suspicious character checker +--SKIPIF-- +<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +--FILE-- +<?php + +$url = "http://www.payp\xD0\xB0l.com"; + +$x = new Spoofchecker(); +echo "paypal with Cyrillic spoof characters\n"; +var_dump($x->isSuspicious($url)); + +echo "certain all-uppercase Latin sequences can be spoof of Greek\n"; +var_dump($x->isSuspicious("NAPKIN PEZ")); +var_dump($x->isSuspicious("napkin pez")); +?> +--EXPECTF-- +paypal with Cyrillic spoof characters +bool(true) +certain all-uppercase Latin sequences can be spoof of Greek +bool(true) +bool(false) diff --git a/ext/intl/tests/spoofchecker_002.phpt b/ext/intl/tests/spoofchecker_002.phpt new file mode 100755 index 0000000000..d570917350 --- /dev/null +++ b/ext/intl/tests/spoofchecker_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +spoofchecker confusable tests +--SKIPIF-- +<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +--FILE-- +<?php + +$url = "http://www.payp\xD0\xB0l.com"; + +$x = new Spoofchecker(); +echo "Checking if words are confusable\n"; +var_dump($x->areConfusable("hello, world", "goodbye, world")); +var_dump($x->areConfusable("hello, world", "hello, world")); +var_dump($x->areConfusable("hello, world", "he11o, wor1d")); +?> +--EXPECTF-- +Checking if words are confusable +bool(false) +bool(true) +bool(true) diff --git a/ext/intl/tests/spoofchecker_003.phpt b/ext/intl/tests/spoofchecker_003.phpt new file mode 100755 index 0000000000..0be9bfa409 --- /dev/null +++ b/ext/intl/tests/spoofchecker_003.phpt @@ -0,0 +1,25 @@ +--TEST-- +spoofchecker with locale settings +--SKIPIF-- +<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +--FILE-- +<?php + +$korean = "\xED\x95\x9C" . "\xEA\xB5\xAD" . "\xEB\xA7\x90"; + +$x = new Spoofchecker(); +echo "Is suspcious, en_US\n"; + +$x->setAllowedLocales('en_US'); +var_dump($x->isSuspicious($korean)); + +echo "Is suspcious, ko_KR\n"; + +$x->setAllowedLocales('en_US, ko_KR'); +var_dump($x->isSuspicious($korean)); +?> +--EXPECTF-- +Is suspcious, en_US +bool(true) +Is suspcious, ko_KR +bool(false) diff --git a/ext/intl/tests/spoofchecker_004.phpt b/ext/intl/tests/spoofchecker_004.phpt new file mode 100755 index 0000000000..b38c61d717 --- /dev/null +++ b/ext/intl/tests/spoofchecker_004.phpt @@ -0,0 +1,28 @@ +--TEST-- +spoofchecker with settings changed +--SKIPIF-- +<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?> +--FILE-- +<?php + +$korean = "\xED\x95\x9C" . "\xEA\xB5\xAD" . "\xEB\xA7\x90"; + +$x = new Spoofchecker(); +echo "Check with default settings\n"; +var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO")); +var_dump($x->areConfusable("hello", "h\xD0\xB5llo")); + +echo "Change confusable settings\n"; +$x->setChecks(Spoofchecker::MIXED_SCRIPT_CONFUSABLE | + Spoofchecker::WHOLE_SCRIPT_CONFUSABLE | + Spoofchecker::SINGLE_SCRIPT_CONFUSABLE); +var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO")); +var_dump($x->areConfusable("hello", "h\xD0\xB5llo")); +?> +--EXPECTF-- +Check with default settings +bool(true) +bool(true) +Change confusable settings +bool(false) +bool(true) diff --git a/ext/intl/tests/transliterator_clone.phpt b/ext/intl/tests/transliterator_clone.phpt new file mode 100644 index 0000000000..23d569b898 --- /dev/null +++ b/ext/intl/tests/transliterator_clone.phpt @@ -0,0 +1,21 @@ +--TEST-- +Transliterator clone handler +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$str = "a U+4E07"; + +$t = Transliterator::create("hex-any"); +echo $t->id, ": ", $t->transliterate($str), "\n"; + +$u = clone $t; +echo $u->id, ": ", $u->transliterate($str), "\n"; + +echo "Done.\n"; + +--EXPECT-- +hex-any: a 万 +hex-any: a 万 +Done. diff --git a/ext/intl/tests/transliterator_create_basic.phpt b/ext/intl/tests/transliterator_create_basic.phpt new file mode 100644 index 0000000000..4de032dbd6 --- /dev/null +++ b/ext/intl/tests/transliterator_create_basic.phpt @@ -0,0 +1,20 @@ +--TEST-- +Transliterator::create (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$t = Transliterator::create("any-latin"); +echo $t->id,"\n"; + +$t = transliterator_create("any-latin"); +echo $t->id,"\n"; + +echo "Done.\n"; + +--EXPECT-- +any-latin +any-latin +Done. + diff --git a/ext/intl/tests/transliterator_create_error.phpt b/ext/intl/tests/transliterator_create_error.phpt new file mode 100644 index 0000000000..31aef68feb --- /dev/null +++ b/ext/intl/tests/transliterator_create_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +Transliterator::create (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); +Transliterator::create("inexistant id"); +echo intl_get_error_message(), "\n"; +Transliterator::create("bad UTF-8 \x8F"); +echo intl_get_error_message(), "\n"; + +echo "Done.\n"; +--EXPECTF-- +Warning: Transliterator::create(): transliterator_create: unable to open ICU transliterator with id "inexistant id" in %s on line %d +transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID + +Warning: Transliterator::create(): String conversion of id to UTF-16 failed in %s on line %d +String conversion of id to UTF-16 failed: U_INVALID_CHAR_FOUND +Done. diff --git a/ext/intl/tests/transliterator_create_from_rule_basic.phpt b/ext/intl/tests/transliterator_create_from_rule_basic.phpt new file mode 100644 index 0000000000..eb8d5da787 --- /dev/null +++ b/ext/intl/tests/transliterator_create_from_rule_basic.phpt @@ -0,0 +1,28 @@ +--TEST-- +Transliterator::createFromRules (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); + +$rules = <<<RULES +α <> y; +\`\` } a > “; +RULES; + +$t = Transliterator::createFromRules($rules); +echo $t->id,"\n"; + +echo $t->transliterate("``akk ``bkk ``aooy"),"\n"; + +$u = transliterator_create_from_rules($rules, Transliterator::REVERSE); + +echo $u->transliterate("``akk ``bkk ``aooy"), "\n"; + +echo "Done.\n"; +--EXPECT-- +RulesTransPHP +“akk ``bkk “aooy +``akk ``bkk ``aooα +Done. diff --git a/ext/intl/tests/transliterator_create_from_rule_error.phpt b/ext/intl/tests/transliterator_create_from_rule_error.phpt new file mode 100644 index 0000000000..cad73ab608 --- /dev/null +++ b/ext/intl/tests/transliterator_create_from_rule_error.phpt @@ -0,0 +1,53 @@ +--TEST-- +Transliterator::createFromRules (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); + +$t = Transliterator::createFromRules(); +echo intl_get_error_message(),"\n"; + +$t = Transliterator::createFromRules("a","b"); +echo intl_get_error_message(),"\n"; + +$t = Transliterator::createFromRules("\x8Fss"); +echo intl_get_error_message(),"\n"; + +$rules = <<<RULES +\`\` } a > “; +\`\` } a > b; +RULES; + +$t = Transliterator::createFromRules($rules); +echo intl_get_error_message(),"\n"; + +$rules = <<<RULES +ffff +RULES; + +$t = Transliterator::createFromRules($rules); +echo intl_get_error_message(),"\n"; +echo "Done.\n"; + +--EXPECTF-- +Warning: Transliterator::createFromRules() expects at least 1 parameter, 0 given in %s on line %d + +Warning: Transliterator::createFromRules(): transliterator_create_from_rules: bad arguments in %s on line %d +transliterator_create_from_rules: bad arguments: U_ILLEGAL_ARGUMENT_ERROR + +Warning: Transliterator::createFromRules() expects parameter 2 to be long, string given in %s on line %d + +Warning: Transliterator::createFromRules(): transliterator_create_from_rules: bad arguments in %s on line %d +transliterator_create_from_rules: bad arguments: U_ILLEGAL_ARGUMENT_ERROR + +Warning: Transliterator::createFromRules(): String conversion of rules to UTF-16 failed in %s on line %d +String conversion of rules to UTF-16 failed: U_INVALID_CHAR_FOUND + +Warning: Transliterator::createFromRules(): transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error after "{'``'}a > “;", before or at "{'``'}a > b;") in %s on line %d +transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error after "{'``'}a > “;", before or at "{'``'}a > b;"): U_RULE_MASK_ERROR + +Warning: Transliterator::createFromRules(): transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error at offset 0, before or at "ffff") in %s on line %d +transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error at offset 0, before or at "ffff"): U_MISSING_OPERATOR +Done. diff --git a/ext/intl/tests/transliterator_create_inverse_basic.phpt b/ext/intl/tests/transliterator_create_inverse_basic.phpt new file mode 100644 index 0000000000..3241403d3d --- /dev/null +++ b/ext/intl/tests/transliterator_create_inverse_basic.phpt @@ -0,0 +1,32 @@ +--TEST-- +Transliterator::createInverse (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); + +$tr = Transliterator::create("Katakana-Latin"); +$orstr = "オーシャンビュー"; +$new_str = $tr->transliterate($orstr); + +$revtr = $tr->createInverse(); +$recovstr = $revtr->transliterate($new_str); + +$revtr2 = transliterator_create_inverse($tr); +$recovstr2 = $revtr2->transliterate($new_str); + +echo $orstr,"\n"; +echo $new_str,"\n"; +echo $recovstr,"\n"; + +var_dump(($orstr == $recovstr) == $recovstr2); + +echo "Done.\n"; +--EXPECT-- +オーシャンビュー +ōshanbyū +オーシャンビュー +bool(true) +Done. diff --git a/ext/intl/tests/transliterator_create_inverse_error.phpt b/ext/intl/tests/transliterator_create_inverse_error.phpt new file mode 100644 index 0000000000..92141c93d4 --- /dev/null +++ b/ext/intl/tests/transliterator_create_inverse_error.phpt @@ -0,0 +1,21 @@ +--TEST-- +Transliterator::createInverse (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); + +$tr = Transliterator::create("Katakana-Latin"); +$tr->createInverse(array()); + +$tr = Transliterator::create("Katakana-Latin"); +transliterator_create_inverse("jj"); + +--EXPECTF-- +Warning: Transliterator::createInverse() expects exactly 0 parameters, 1 given in %s on line %d + +Warning: Transliterator::createInverse(): transliterator_create_inverse: bad arguments in %s on line %d + +Catchable fatal error: Argument 1 passed to transliterator_create_inverse() must be an instance of Transliterator, string given in %s on line %d diff --git a/ext/intl/tests/transliterator_get_error_code_basic.phpt b/ext/intl/tests/transliterator_get_error_code_basic.phpt new file mode 100644 index 0000000000..3d16a8a9a0 --- /dev/null +++ b/ext/intl/tests/transliterator_get_error_code_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +Transliterator::getErrorCode (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex"); +var_dump($t->transliterate("\x8F")); +echo transliterator_get_error_code($t), "\n"; + +echo $t->getErrorCode(), "\n"; + +var_dump($t->transliterate("")); +echo $t->getErrorCode(), "\n"; + +echo "Done.\n"; +--EXPECTF-- +Warning: Transliterator::transliterate(): String conversion of string to UTF-16 failed in %s on line %d +bool(false) +10 +10 +string(0) "" +0 +Done. diff --git a/ext/intl/tests/transliterator_get_error_code_error.phpt b/ext/intl/tests/transliterator_get_error_code_error.phpt new file mode 100644 index 0000000000..94daa120d7 --- /dev/null +++ b/ext/intl/tests/transliterator_get_error_code_error.phpt @@ -0,0 +1,24 @@ +--TEST-- +Transliterator::getErrorCode (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex"); +echo transliterator_get_error_code(), "\n"; +echo $t->getErrorCode(null), "\n"; +echo transliterator_get_error_code(array()), "\n"; + +--EXPECTF-- +Warning: transliterator_get_error_code() expects exactly 1 parameter, 0 given in %s on line %d + +Warning: transliterator_get_error_code(): transliterator_get_error_code: unable to parse input params in %s on line %d + + +Warning: Transliterator::getErrorCode() expects exactly 0 parameters, 1 given in %s on line %d + +Warning: Transliterator::getErrorCode(): transliterator_get_error_code: unable to parse input params in %s on line %d + + +Catchable fatal error: Argument 1 passed to transliterator_get_error_code() must be an instance of Transliterator, array given in %s on line %d diff --git a/ext/intl/tests/transliterator_get_error_message_basic.phpt b/ext/intl/tests/transliterator_get_error_message_basic.phpt new file mode 100644 index 0000000000..4e918530b2 --- /dev/null +++ b/ext/intl/tests/transliterator_get_error_message_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +Transliterator::getErrorMessage (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex"); +var_dump($t->transliterate("\x8F")); +echo transliterator_get_error_message($t), "\n"; + +echo $t->getErrorMessage(), "\n"; + +var_dump($t->transliterate("")); +echo $t->getErrorMessage(), "\n"; + +echo "Done.\n"; +--EXPECTF-- +Warning: Transliterator::transliterate(): String conversion of string to UTF-16 failed in %s on line %d +bool(false) +String conversion of string to UTF-16 failed: U_INVALID_CHAR_FOUND +String conversion of string to UTF-16 failed: U_INVALID_CHAR_FOUND +string(0) "" +U_ZERO_ERROR +Done. diff --git a/ext/intl/tests/transliterator_get_error_message_error.phpt b/ext/intl/tests/transliterator_get_error_message_error.phpt new file mode 100644 index 0000000000..4ab21045b2 --- /dev/null +++ b/ext/intl/tests/transliterator_get_error_message_error.phpt @@ -0,0 +1,24 @@ +--TEST-- +Transliterator::getErrorMessage (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex"); +echo transliterator_get_error_message(), "\n"; +echo $t->getErrorMessage(null), "\n"; +echo transliterator_get_error_message(array()), "\n"; + +--EXPECTF-- +Warning: transliterator_get_error_message() expects exactly 1 parameter, 0 given in %s on line %d + +Warning: transliterator_get_error_message(): transliterator_get_error_message: unable to parse input params in %s on line %d + + +Warning: Transliterator::getErrorMessage() expects exactly 0 parameters, 1 given in %s on line %d + +Warning: Transliterator::getErrorMessage(): transliterator_get_error_message: unable to parse input params in %s on line %d + + +Catchable fatal error: Argument 1 passed to transliterator_get_error_message() must be an instance of Transliterator, array given in %s on line %d diff --git a/ext/intl/tests/transliterator_list_ids_basic.phpt b/ext/intl/tests/transliterator_list_ids_basic.phpt new file mode 100644 index 0000000000..af65b5853e --- /dev/null +++ b/ext/intl/tests/transliterator_list_ids_basic.phpt @@ -0,0 +1,16 @@ +--TEST-- +Transliterator::listIDs (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); +var_dump(count(transliterator_list_ids()) > 100); +var_dump(count(Transliterator::listIDs()) > 100); + +echo "Done.\n"; +--EXPECT-- +bool(true) +bool(true) +Done. diff --git a/ext/intl/tests/transliterator_list_ids_error.phpt b/ext/intl/tests/transliterator_list_ids_error.phpt new file mode 100644 index 0000000000..d1066a8920 --- /dev/null +++ b/ext/intl/tests/transliterator_list_ids_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Transliterator::listIDs (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); +var_dump(transliterator_list_ids(array())); + +echo "Done.\n"; + +--EXPECTF-- +Warning: transliterator_list_ids() expects exactly 0 parameters, 1 given in %s on line %d + +Warning: transliterator_list_ids(): transliterator_list_ids: bad arguments in %s on line %d +bool(false) +Done. diff --git a/ext/intl/tests/transliterator_property_id.phpt b/ext/intl/tests/transliterator_property_id.phpt new file mode 100644 index 0000000000..b5337b68e6 --- /dev/null +++ b/ext/intl/tests/transliterator_property_id.phpt @@ -0,0 +1,21 @@ +--TEST-- +Transliterator - "id" property +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$tr = Transliterator::create("Katakana-Latin"); +echo $tr->id, "\n"; +$revtr = $tr->createInverse(); +echo $revtr->id, "\n"; +var_dump($revtr); + +echo "Done.\n"; +--EXPECTF-- +Katakana-Latin +Latin-Katakana +object(Transliterator)#%d (%d) { + ["id"]=> + string(%d) "Latin-Katakana" +} +Done. diff --git a/ext/intl/tests/transliterator_transliterate_basic.phpt b/ext/intl/tests/transliterator_transliterate_basic.phpt new file mode 100644 index 0000000000..f117834781 --- /dev/null +++ b/ext/intl/tests/transliterator_transliterate_basic.phpt @@ -0,0 +1,20 @@ +--TEST-- +Transliterator::transliterate (basic) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +$t = transliterator_create("Latin; Title"); +$s = "Κοντογιαννάτος, Βασίλης"; +echo $t->transliterate($s),"\n"; +echo transliterator_transliterate($t, $s),"\n"; +echo $t->transliterate($s, 3),"\n"; +echo $t->transliterate($s, 3, 4),"\n"; + +echo "Done.\n"; +--EXPECT-- +Kontogiannátos, Basílēs +Kontogiannátos, Basílēs +ΚονTogiannátos, Basílēs +ΚονTογιαννάτος, Βασίλης +Done. diff --git a/ext/intl/tests/transliterator_transliterate_error.phpt b/ext/intl/tests/transliterator_transliterate_error.phpt new file mode 100644 index 0000000000..cdddcfb87b --- /dev/null +++ b/ext/intl/tests/transliterator_transliterate_error.phpt @@ -0,0 +1,60 @@ +--TEST-- +Transliterator::transliterate (error) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php + +ini_set("intl.error_level", E_WARNING); + +$tr = Transliterator::create("latin"); + +//Arguments +var_dump(transliterator_transliterate()); +var_dump(transliterator_transliterate($tr,array())); +var_dump(transliterator_transliterate($tr,"str",7)); +var_dump(transliterator_transliterate($tr,"str",7,6)); +var_dump(transliterator_transliterate($tr,"str",2,-1,"extra")); + +//Arguments +var_dump($tr->transliterate()); +var_dump($tr->transliterate(array())); + +//bad UTF-8 +transliterator_transliterate($tr, "\x80\x03"); + +echo "Done.\n"; +--EXPECTF-- +Warning: transliterator_transliterate() expects at least 2 parameters, 0 given in %s on line %d + +Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d +bool(false) + +Warning: transliterator_transliterate() expects parameter 2 to be string, array given in %s on line %d + +Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d +bool(false) + +Warning: transliterator_transliterate(): transliterator_transliterate: Neither "start" nor the "end" arguments can exceed the number of UTF-16 code units (in this case, 3) in %s on line %d +bool(false) + +Warning: transliterator_transliterate(): transliterator_transliterate: "start" argument should be non-negative and not bigger than "end" (if defined) in %s on line %d +bool(false) + +Warning: transliterator_transliterate() expects at most 4 parameters, 5 given in %s on line %d + +Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d +bool(false) + +Warning: Transliterator::transliterate() expects at least 1 parameter, 0 given in %s on line %d + +Warning: Transliterator::transliterate(): transliterator_transliterate: bad arguments in %s on line %d +bool(false) + +Warning: Transliterator::transliterate() expects parameter 1 to be string, array given in %s on line %d + +Warning: Transliterator::transliterate(): transliterator_transliterate: bad arguments in %s on line %d +bool(false) + +Warning: transliterator_transliterate(): String conversion of string to UTF-16 failed in %s on line %d +Done. diff --git a/ext/intl/tests/transliterator_transliterate_variant1.phpt b/ext/intl/tests/transliterator_transliterate_variant1.phpt new file mode 100644 index 0000000000..fc77a4e3ac --- /dev/null +++ b/ext/intl/tests/transliterator_transliterate_variant1.phpt @@ -0,0 +1,37 @@ +--TEST-- +transliterator_transliterate (variant 1, non-transliterator 1st arg) +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +//exec('pause'); +$str = " o"; +echo transliterator_transliterate("[\p{White_Space}] hex", $str), "\n"; + +echo transliterator_transliterate("\x8F", $str), "\n"; +echo intl_get_error_message(), "\n"; + +class A { +function __toString() { return "inexistant id"; } +} + +echo transliterator_transliterate(new A(), $str), "\n"; +echo intl_get_error_message(), "\n"; + +echo "Done.\n"; +--EXPECTF-- +\u0020o + +Warning: transliterator_transliterate(): String conversion of id to UTF-16 failed in %s on line %d + +Warning: transliterator_transliterate(): Could not create transliterator with ID %s + +String conversion of id to UTF-16 failed: U_INVALID_CHAR_FOUND + +Warning: transliterator_transliterate(): transliterator_create: unable to open ICU transliterator with id "inexistant id" in %s on line %d + +Warning: transliterator_transliterate(): Could not create transliterator with ID "inexistant id" (transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID) in %s on line %d + +transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID +Done. diff --git a/ext/intl/transliterator/transliterator.c b/ext/intl/transliterator/transliterator.c new file mode 100644 index 0000000000..75c9eaabda --- /dev/null +++ b/ext/intl/transliterator/transliterator.c @@ -0,0 +1,138 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "transliterator_class.h" +#include "transliterator.h" +#include "intl_convert.h" + +#include <unicode/ustring.h> + +/* {{{ transliterator_register_constants + * Register constants common for both (OO and procedural) APIs. + */ +void transliterator_register_constants( INIT_FUNC_ARGS ) +{ + if( !Transliterator_ce_ptr ) + { + zend_error( E_ERROR, "Transliterator class not defined" ); + return; + } + + #define TRANSLITERATOR_EXPOSE_CONST( x ) REGISTER_LONG_CONSTANT( #x, x, CONST_CS ) + #define TRANSLITERATOR_EXPOSE_CLASS_CONST( x ) zend_declare_class_constant_long( Transliterator_ce_ptr, ZEND_STRS( #x ) - 1, TRANSLITERATOR_##x TSRMLS_CC ); + #define TRANSLITERATOR_EXPOSE_CUSTOM_CLASS_CONST( name, value ) zend_declare_class_constant_long( Transliterator_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );*/ + + /* Normalization form constants */ + TRANSLITERATOR_EXPOSE_CLASS_CONST( FORWARD ); + TRANSLITERATOR_EXPOSE_CLASS_CONST( REVERSE ); + + #undef NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST + #undef NORMALIZER_EXPOSE_CLASS_CONST + #undef NORMALIZER_EXPOSE_CONST +} +/* }}} */ + +/* {{{ transliterator_parse_error_to_string + * Transforms parse errors in strings. + */ +smart_str transliterator_parse_error_to_string( UParseError* pe ) +{ + smart_str ret = {0}; + char *buf; + int u8len; + UErrorCode status; + int any = 0; + + assert( pe != NULL ); + + smart_str_appends( &ret, "parse error " ); + if( pe->line > 0 ) + { + smart_str_appends( &ret, "on line " ); + smart_str_append_long( &ret, (long ) pe->line ); + any = 1; + } + if( pe->offset >= 0 ) { + if( any ) + smart_str_appends( &ret, ", " ); + else + smart_str_appends( &ret, "at " ); + + smart_str_appends( &ret, "offset " ); + smart_str_append_long( &ret, (long ) pe->offset ); + any = 1; + } + + if (pe->preContext[0] != 0 ) { + if( any ) + smart_str_appends( &ret, ", " ); + + smart_str_appends( &ret, "after \"" ); + intl_convert_utf16_to_utf8( &buf, &u8len, pe->preContext, -1, &status ); + if( U_FAILURE( status ) ) + { + smart_str_appends( &ret, "(could not convert parser error pre-context to UTF-8)" ); + } + else { + smart_str_appendl( &ret, buf, u8len ); + efree( buf ); + } + smart_str_appends( &ret, "\"" ); + any = 1; + } + + if( pe->postContext[0] != 0 ) + { + if( any ) + smart_str_appends( &ret, ", " ); + + smart_str_appends( &ret, "before or at \"" ); + intl_convert_utf16_to_utf8( &buf, &u8len, pe->postContext, -1, &status ); + if( U_FAILURE( status ) ) + { + smart_str_appends( &ret, "(could not convert parser error post-context to UTF-8)" ); + } + else + { + smart_str_appendl( &ret, buf, u8len ); + efree( buf ); + } + smart_str_appends( &ret, "\"" ); + any = 1; + } + + if( !any ) + { + smart_str_free( &ret ); + smart_str_appends( &ret, "no parse error" ); + } + + smart_str_0( &ret ); + return ret; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/transliterator/transliterator.h b/ext/intl/transliterator/transliterator.h new file mode 100644 index 0000000000..cfd5d38dbd --- /dev/null +++ b/ext/intl/transliterator/transliterator.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@netcabo.ot> | + +----------------------------------------------------------------------+ + */ + +#ifndef TRANSLITERATOR_TRANSLITERATOR_H +#define TRANSLITERATOR_TRANSLITERATOR_H + +#include <php.h> +#include <unicode/utypes.h> +#include <unicode/utrans.h> + +#include "ext/standard/php_smart_str.h" + +void transliterator_register_constants( INIT_FUNC_ARGS ); +smart_str transliterator_parse_error_to_string( UParseError* pe ); + +#endif /* #ifndef TRANSLITERATOR_TRANSLITERATOR_H */ diff --git a/ext/intl/transliterator/transliterator_class.c b/ext/intl/transliterator/transliterator_class.c new file mode 100644 index 0000000000..8d4d0649e9 --- /dev/null +++ b/ext/intl/transliterator/transliterator_class.c @@ -0,0 +1,434 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@php.net> | + +----------------------------------------------------------------------+ + */ + +#include "transliterator_class.h" +#include "php_intl.h" +#include "transliterator_methods.h" +#include "intl_error.h" +#include "intl_convert.h" +#include "intl_data.h" + +#include <unicode/utrans.h> + +zend_class_entry *Transliterator_ce_ptr = NULL; + +zend_object_handlers Transliterator_handlers; + +/* {{{ int transliterator_object_construct( zval *object, UTransliterator *utrans, UErrorCode *status TSRMLS_DC ) + * Initialize internals of Transliterator_object. + */ +int transliterator_object_construct( zval *object, + UTransliterator *utrans, + UErrorCode *status TSRMLS_DC ) +{ + const UChar *ustr_id; + int32_t ustr_id_len; + char *str_id; + int str_id_len; + Transliterator_object *to; + + TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; + + assert( to->utrans == NULL ); + /* this assignment must happen before any return with failure because the + * caller relies on it always being made (so it can just destroy the object + * to close the transliterator) */ + to->utrans = utrans; + + ustr_id = utrans_getUnicodeID( utrans, &ustr_id_len ); + intl_convert_utf16_to_utf8( &str_id, &str_id_len, ustr_id, (int ) ustr_id_len, status ); + if( U_FAILURE( *status ) ) + { + return FAILURE; + } + + zend_update_property_stringl( Transliterator_ce_ptr, object, + "id", sizeof( "id" ) - 1, str_id, str_id_len TSRMLS_CC ); + efree( str_id ); + return SUCCESS; +} +/* }}} */ + +/* + * Auxiliary functions needed by objects of 'Transliterator' class + */ + +/* {{{ void transliterator_object_init( Transliterator_object* to ) + * Initialize internals of Transliterator_object. + */ +static void transliterator_object_init( Transliterator_object* to TSRMLS_DC ) +{ + if( !to ) + return; + + intl_error_init( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC ); +} +/* }}} */ + +/* {{{ void transliterator_object_destroy( Transliterator_object* to ) + * Clean up mem allocted by internals of Transliterator_object + */ +static void transliterator_object_destroy( Transliterator_object* to TSRMLS_DC ) +{ + if( !to ) + return; + + if( to->utrans ) + { + utrans_close( to->utrans ); + to->utrans = NULL; + } + + intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC ); +} +/* }}} */ + +/* {{{ Transliterator_objects_dtor */ +static void Transliterator_objects_dtor( + void *object, + zend_object_handle handle TSRMLS_DC ) +{ + zend_objects_destroy_object( object, handle TSRMLS_CC ); +} +/* }}} */ + +/* {{{ Transliterator_objects_free */ +static void Transliterator_objects_free( zend_object *object TSRMLS_DC ) +{ + Transliterator_object* to = (Transliterator_object*) object; + + zend_object_std_dtor( &to->zo TSRMLS_CC ); + + transliterator_object_destroy( to TSRMLS_CC ); + + efree( to ); +} +/* }}} */ + +/* {{{ Transliterator_object_create */ +static zend_object_value Transliterator_object_create( + zend_class_entry *ce TSRMLS_DC ) +{ + zend_object_value retval; + Transliterator_object* intern; + + intern = ecalloc( 1, sizeof( Transliterator_object ) ); + + zend_object_std_init( &intern->zo, ce TSRMLS_CC ); +#if PHP_VERSION_ID < 50399 + zend_hash_copy( intern->zo.properties, &(ce->default_properties ), + (copy_ctor_func_t) zval_add_ref, NULL, sizeof( zval* ) ); +#else + object_properties_init( (zend_object*) intern, ce ); +#endif + transliterator_object_init( intern TSRMLS_CC ); + + retval.handle = zend_objects_store_put( + intern, + Transliterator_objects_dtor, + (zend_objects_free_object_storage_t) Transliterator_objects_free, + NULL TSRMLS_CC ); + + retval.handlers = &Transliterator_handlers; + + return retval; +} +/* }}} */ + +/* + * Object handlers for Transliterator class (and subclasses) + */ + +/* {{{ clone handler for Transliterator */ +static zend_object_value Transliterator_clone_obj( zval *object TSRMLS_DC ) +{ + Transliterator_object *to_orig, + *to_new; + zend_object_value ret_val; + intl_error_reset( NULL TSRMLS_CC ); + + to_orig = zend_object_store_get_object( object TSRMLS_CC ); + intl_error_reset( INTL_DATA_ERROR_P( to_orig ) TSRMLS_CC ); + ret_val = Transliterator_ce_ptr->create_object( Transliterator_ce_ptr TSRMLS_CC ); + to_new = zend_object_store_get_object_by_handle( ret_val.handle TSRMLS_CC ); + + zend_objects_clone_members( &to_new->zo, ret_val, + &to_orig->zo, Z_OBJ_HANDLE_P( object ) TSRMLS_CC ); + + if( to_orig->utrans != NULL ) + { + UTransliterator *utrans = NULL; + zval tempz; /* dummy zval to pass to transliterator_object_construct */ + + /* guaranteed to return NULL if it fails */ + utrans = utrans_clone( to_orig->utrans, TRANSLITERATOR_ERROR_CODE_P( to_orig ) ); + + if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to_orig ) ) ) + goto err; + + Z_OBJVAL( tempz ) = ret_val; + transliterator_object_construct( &tempz, utrans, + TRANSLITERATOR_ERROR_CODE_P( to_orig ) TSRMLS_CC ); + + if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to_orig ) ) ) + { + char *err_msg; +err: + + if( utrans != NULL ) + transliterator_object_destroy( to_new TSRMLS_CC ); + + /* set the error anyway, in case in the future we decide not to + * throw an error. It also helps build the error message */ + intl_error_set_code( NULL, INTL_DATA_ERROR_CODE( to_orig ) TSRMLS_CC ); + intl_errors_set_custom_msg( TRANSLITERATOR_ERROR_P( to_orig ), + "Could not clone transliterator", 0 TSRMLS_CC ); + + err_msg = intl_error_get_message( TRANSLITERATOR_ERROR_P( to_orig ) TSRMLS_CC ); + php_error_docref( NULL TSRMLS_CC, E_ERROR, "%s", err_msg ); + efree( err_msg ); /* if it's changed into a warning */ + /* do not destroy tempz; we need to return something */ + } + } + else + { + /* We shouldn't have unconstructed objects in the first place */ + php_error_docref( NULL TSRMLS_CC, E_WARNING, + "Cloning unconstructed transliterator." ); + } + + return ret_val; +} +/* }}} */ + +#if PHP_VERSION_ID >= 50399 +# define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG \ + zval tmp_member; \ + if( Z_TYPE_P( member ) != IS_STRING ) \ + { \ + tmp_member = *member; \ + zval_copy_ctor( &tmp_member ); \ + convert_to_string( &tmp_member ); \ + member = &tmp_member; \ + key = NULL; \ + } +#else +# define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG \ + zval tmp_member; \ + if( Z_TYPE_P( member ) != IS_STRING ) \ + { \ + tmp_member = *member; \ + zval_copy_ctor( &tmp_member ); \ + convert_to_string( &tmp_member ); \ + member = &tmp_member; \ + } +#endif + +#define TRANSLITERATOR_PROPERTY_HANDLER_EPILOG \ + if( member == &tmp_member ) \ + { \ + zval_dtor( &tmp_member ); \ + } + +/* {{{ get_property_ptr_ptr handler */ +#if PHP_VERSION_ID < 50399 +static zval **Transliterator_get_property_ptr_ptr( zval *object, zval *member TSRMLS_DC ) +#else +static zval **Transliterator_get_property_ptr_ptr( zval *object, zval *member, + const struct _zend_literal *key TSRMLS_DC ) +#endif +{ + zval **retval; + + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + + if(zend_binary_strcmp( "id", sizeof( "id" ) - 1, + Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) + { + retval = NULL; /* fallback to read_property */ + } + else + { +#if PHP_VERSION_ID < 50399 + retval = std_object_handlers.get_property_ptr_ptr( object, member TSRMLS_CC ); +#else + retval = std_object_handlers.get_property_ptr_ptr( object, member, key TSRMLS_CC ); +#endif + } + + TRANSLITERATOR_PROPERTY_HANDLER_EPILOG; + + return retval; +} +/* }}} */ + +/* {{{ read_property handler */ +#if PHP_VERSION_ID < 50399 +static zval *Transliterator_read_property( zval *object, zval *member, int type TSRMLS_DC ) /* {{{ */ +#else +static zval *Transliterator_read_property( zval *object, zval *member, int type, + const struct _zend_literal *key TSRMLS_DC ) /* {{{ */ +#endif +{ + zval *retval; + + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + + if( ( type != BP_VAR_R && type != BP_VAR_IS ) && + ( zend_binary_strcmp( "id", sizeof( "id" ) - 1, + Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) ) + { + php_error_docref0( NULL TSRMLS_CC, E_WARNING, "The property \"id\" is read-only" ); + retval = &EG( uninitialized_zval ); + } + else + { +#if PHP_VERSION_ID < 50399 + retval = std_object_handlers.read_property( object, member, type TSRMLS_CC ); +#else + retval = std_object_handlers.read_property( object, member, type, key TSRMLS_CC ); +#endif + } + + TRANSLITERATOR_PROPERTY_HANDLER_EPILOG; + + return retval; +} + +/* }}} */ + +/* {{{ write_property handler */ +#if PHP_VERSION_ID < 50399 +static void Transliterator_write_property( zval *object, zval *member, zval *value TSRMLS_DC ) +#else +static void Transliterator_write_property( zval *object, zval *member, zval *value, + const struct _zend_literal *key TSRMLS_DC ) +#endif +{ + TRANSLITERATOR_PROPERTY_HANDLER_PROLOG; + + if( ( EG( scope ) != Transliterator_ce_ptr ) && + ( zend_binary_strcmp( "id", sizeof( "id" ) - 1, + Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) ) + { + php_error_docref0( NULL TSRMLS_CC, E_WARNING, "The property \"id\" is read-only" ); + } + else + { +#if PHP_VERSION_ID < 50399 + std_object_handlers.write_property( object, member, value TSRMLS_CC ); +#else + std_object_handlers.write_property( object, member, value, key TSRMLS_CC ); +#endif + } + + TRANSLITERATOR_PROPERTY_HANDLER_EPILOG; +} +/* }}} */ + +/* + * 'Transliterator' class registration structures & functions + */ + +/* {{{ Transliterator methods arguments info */ + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_void, 0, 0, 0 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create, 0, 0, 1 ) + ZEND_ARG_INFO( 0, id ) + ZEND_ARG_INFO( 0, direction ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_from_rules, 0, 0, 1 ) + ZEND_ARG_INFO( 0, rules ) + ZEND_ARG_INFO( 0, direction ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_inverse, 0, 0, 1 ) + ZEND_ARG_OBJ_INFO( 0, orig_trans, Transliterator, 0 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_me_transliterate, 0, 0, 1 ) + ZEND_ARG_INFO( 0, subject ) + ZEND_ARG_INFO( 0, start ) + ZEND_ARG_INFO( 0, end ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_error, 0, 0, 1 ) + ZEND_ARG_OBJ_INFO( 0, trans, Transliterator, 0 ) +ZEND_END_ARG_INFO() + +/* }}} */ + +/* {{{ Transliterator_class_functions + * Every 'Transliterator' class method has an entry in this table + */ +zend_function_entry Transliterator_class_functions[] = { + PHP_ME( Transliterator, __construct, ainfo_trans_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR | ZEND_ACC_FINAL ) + PHP_ME_MAPPING( create, transliterator_create, ainfo_trans_create, ZEND_ACC_STATIC |ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( createFromRules,transliterator_create_from_rules, ainfo_trans_create_from_rules, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( createInverse, transliterator_create_inverse, ainfo_trans_void, ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( listIDs, transliterator_list_ids, ainfo_trans_void, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( transliterate, transliterator_transliterate, ainfo_trans_me_transliterate, ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( getErrorCode, transliterator_get_error_code, ainfo_trans_void, ZEND_ACC_PUBLIC ) + PHP_ME_MAPPING( getErrorMessage,transliterator_get_error_message, ainfo_trans_void, ZEND_ACC_PUBLIC ) + PHP_FE_END +}; +/* }}} */ + +/* {{{ transliterator_register_Transliterator_class + * Initialize 'Transliterator' class + */ +void transliterator_register_Transliterator_class( TSRMLS_D ) +{ + zend_class_entry ce; + + /* Create and register 'Transliterator' class. */ + INIT_CLASS_ENTRY( ce, "Transliterator", Transliterator_class_functions ); + ce.create_object = Transliterator_object_create; + Transliterator_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC ); + memcpy( &Transliterator_handlers, zend_get_std_object_handlers(), + sizeof Transliterator_handlers ); + Transliterator_handlers.clone_obj = Transliterator_clone_obj; + Transliterator_handlers.get_property_ptr_ptr = Transliterator_get_property_ptr_ptr; + Transliterator_handlers.read_property = Transliterator_read_property; + Transliterator_handlers.write_property = Transliterator_write_property; + + /* Declare 'Transliterator' class properties */ + if( !Transliterator_ce_ptr ) + { + zend_error( E_ERROR, + "Transliterator: attempt to create properties " + "on a non-registered class." ); + return; + } + zend_declare_property_null( Transliterator_ce_ptr, + "id", sizeof( "id" ) - 1, ZEND_ACC_PUBLIC TSRMLS_CC ); + + /* constants are declared in transliterator_register_constants, called from MINIT */ + +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/transliterator/transliterator_class.h b/ext/intl/transliterator/transliterator_class.h new file mode 100644 index 0000000000..5ca50ed2f4 --- /dev/null +++ b/ext/intl/transliterator/transliterator_class.h @@ -0,0 +1,65 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@netcabo.pt> | + +----------------------------------------------------------------------+ + */ + +#ifndef TRANSLITERATOR_CLASS_H +#define TRANSLITERATOR_CLASS_H + +#include <php.h> + +#include "intl_common.h" +#include "intl_error.h" + +#include <unicode/utrans.h> + +typedef struct { + zend_object zo; + + // error handling + intl_error err; + + // ICU transliterator + UTransliterator* utrans; +} Transliterator_object; + +#define TRANSLITERATOR_FORWARD UTRANS_FORWARD +#define TRANSLITERATOR_REVERSE UTRANS_REVERSE + +#define TRANSLITERATOR_ERROR( co ) (co)->err +#define TRANSLITERATOR_ERROR_P( co ) &(TRANSLITERATOR_ERROR( co )) + +#define TRANSLITERATOR_ERROR_CODE( co ) INTL_ERROR_CODE(TRANSLITERATOR_ERROR( co )) +#define TRANSLITERATOR_ERROR_CODE_P( co ) &(INTL_ERROR_CODE(TRANSLITERATOR_ERROR( co ))) + +#define TRANSLITERATOR_METHOD_INIT_VARS INTL_METHOD_INIT_VARS( Transliterator, to ) +#define TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT( Transliterator, to ) +#define TRANSLITERATOR_METHOD_FETCH_OBJECT\ + TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; \ + if( to->utrans == NULL ) \ + { \ + intl_errors_set( &to->err, U_ILLEGAL_ARGUMENT_ERROR, "Found unconstructed transliterator", 0 TSRMLS_CC ); \ + RETURN_FALSE; \ + } + +int transliterator_object_construct( zval *object, + UTransliterator *utrans, + UErrorCode *status TSRMLS_DC ); + +void transliterator_register_Transliterator_class( TSRMLS_D ); + +extern zend_class_entry *Transliterator_ce_ptr; +extern zend_object_handlers Transliterator_handlers; + +#endif /* #ifndef TRANSLITERATOR_CLASS_H */ diff --git a/ext/intl/transliterator/transliterator_methods.c b/ext/intl/transliterator/transliterator_methods.c new file mode 100644 index 0000000000..d0cfb9790d --- /dev/null +++ b/ext/intl/transliterator/transliterator_methods.c @@ -0,0 +1,542 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "transliterator.h" +#include "transliterator_class.h" +#include "transliterator_methods.h" +#include "intl_data.h" +#include "intl_convert.h" + +#include <zend_exceptions.h> + +static int create_transliterator( char *str_id, int str_id_len, long direction, zval *object TSRMLS_DC ) +{ + Transliterator_object *to; + UChar *ustr_id = NULL; + int32_t ustr_id_len = 0; + UTransliterator *utrans; + UParseError parse_error = {0, -1}; + + intl_error_reset( NULL TSRMLS_CC ); + + if( ( direction != TRANSLITERATOR_FORWARD ) && (direction != TRANSLITERATOR_REVERSE ) ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_create: invalid direction", 0 TSRMLS_CC ); + return FAILURE; + } + + object_init_ex( object, Transliterator_ce_ptr ); + TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; /* fetch zend object from zval "object" into "to" */ + + /* Convert transliterator id to UTF-16 */ + intl_convert_utf8_to_utf16( &ustr_id, &ustr_id_len, str_id, str_id_len, TRANSLITERATOR_ERROR_CODE_P( to ) ); + if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) ) + { + intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC ); + intl_error_set_custom_msg( NULL, "String conversion of id to UTF-16 failed", 0 TSRMLS_CC ); + zval_dtor( object ); + return FAILURE; + } + + /* Open ICU Transliterator. */ + utrans = utrans_openU( ustr_id, ustr_id_len, (UTransDirection ) direction, + NULL, -1, &parse_error, TRANSLITERATOR_ERROR_CODE_P( to ) ); + if (ustr_id) { + efree( ustr_id ); + } + + if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) ) + { + char *buf = NULL; + intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC ); + spprintf( &buf, 0, "transliterator_create: unable to open ICU transliterator" + " with id \"%s\"", str_id ); + if( buf == NULL ) { + intl_error_set_custom_msg( NULL, + "transliterator_create: unable to open ICU transliterator", 0 TSRMLS_CC ); + } + else + { + intl_error_set_custom_msg( NULL, buf, /* copy message */ 1 TSRMLS_CC ); + efree( buf ); + } + zval_dtor( object ); + return FAILURE; + } + + transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC ); + /* no need to close the transliterator manually on construction error */ + if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) ) + { + intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC ); + intl_error_set_custom_msg( NULL, + "transliterator_create: internal constructor call failed", 0 TSRMLS_CC ); + zval_dtor( object ); + return FAILURE; + } + + return SUCCESS; +} + +/* {{{ proto Transliterator transliterator_create( string id [, int direction ] ) + * proto Transliterator Transliterator::create( string id [, int direction ] ) + * Opens a transliterator by id. + */ +PHP_FUNCTION( transliterator_create ) +{ + char *str_id; + int str_id_len; + long direction = TRANSLITERATOR_FORWARD; + int res; + + TRANSLITERATOR_METHOD_INIT_VARS; + + (void) to; /* unused */ + + if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|l", + &str_id, &str_id_len, &direction ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_create: bad arguments", 0 TSRMLS_CC ); + RETURN_NULL(); + } + + object = return_value; + res = create_transliterator( str_id, str_id_len, direction, object TSRMLS_CC ); + if( res == FAILURE ) + RETURN_NULL(); + + /* success, leave return_value as it is (set by create_transliterator) */ +} +/* }}} */ + +/* {{{ proto Transliterator transliterator_create_from_rules( string rules [, int direction ] ) + * proto Transliterator Transliterator::createFromRules( string rules [, int direction ] ) + * Opens a transliterator by id. + */ +PHP_FUNCTION( transliterator_create_from_rules ) +{ + char *str_rules; + int str_rules_len; + UChar *ustr_rules = NULL; + int32_t ustr_rules_len = 0; + long direction = TRANSLITERATOR_FORWARD; + UParseError parse_error = {0, -1}; + UTransliterator *utrans; + UChar id[] = {0x52, 0x75, 0x6C, 0x65, 0x73, 0x54, 0x72, + 0x61, 0x6E, 0x73, 0x50, 0x48, 0x50, 0}; /* RulesTransPHP */ + TRANSLITERATOR_METHOD_INIT_VARS; + + if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|l", + &str_rules, &str_rules_len, &direction ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_create_from_rules: bad arguments", 0 TSRMLS_CC ); + RETURN_NULL(); + } + + if( ( direction != TRANSLITERATOR_FORWARD ) && (direction != TRANSLITERATOR_REVERSE ) ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_create_from_rules: invalid direction", 0 TSRMLS_CC ); + RETURN_NULL(); + } + + object = return_value; + object_init_ex( object, Transliterator_ce_ptr ); + TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; + + intl_convert_utf8_to_utf16( &ustr_rules, &ustr_rules_len, + str_rules, str_rules_len, TRANSLITERATOR_ERROR_CODE_P( to ) ); + /* (I'm not a big fan of non-obvious flow control macros ). + * This one checks the error value, destroys object and returns false */ + INTL_CTOR_CHECK_STATUS( to, "String conversion of rules to UTF-16 failed" ); + + /* Open ICU Transliterator. */ + utrans = utrans_openU( id, ( sizeof( id ) - 1 ) / ( sizeof( *id ) ), (UTransDirection ) direction, + ustr_rules, ustr_rules_len, &parse_error, TRANSLITERATOR_ERROR_CODE_P( to ) ); + if (ustr_rules) { + efree( ustr_rules ); + } + + intl_error_set_code( NULL, INTL_DATA_ERROR_CODE( to ) TSRMLS_CC ); + if( U_FAILURE( INTL_DATA_ERROR_CODE( to ) ) ) + { + char *msg = NULL; + smart_str parse_error_str; + parse_error_str = transliterator_parse_error_to_string( &parse_error ); + spprintf( &msg, 0, "transliterator_create_from_rules: unable to " + "create ICU transliterator from rules (%s)", parse_error_str.c ); + smart_str_free( &parse_error_str ); + if( msg != NULL ) + { + intl_errors_set_custom_msg( INTL_DATA_ERROR_P( to ), msg, 1 TSRMLS_CC ); + efree( msg ); + } + zval_dtor( return_value ); + RETURN_NULL(); + } + transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC ); + /* no need to close the transliterator manually on construction error */ + INTL_CTOR_CHECK_STATUS( to, "transliterator_create_from_rules: internal constructor call failed" ); +} +/* }}} */ + +/* {{{ proto Transliterator transliterator_create_inverse( Transliterator orig_trans ) + * proto Transliterator Transliterator::createInverse() + * Opens the inverse transliterator transliterator. + */ +PHP_FUNCTION( transliterator_create_inverse ) +{ + Transliterator_object *to_orig; + UTransliterator *utrans; + TRANSLITERATOR_METHOD_INIT_VARS; + + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Transliterator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_create_inverse: bad arguments", 0 TSRMLS_CC ); + RETURN_NULL(); + } + + TRANSLITERATOR_METHOD_FETCH_OBJECT; + to_orig = to; + + object = return_value; + object_init_ex( object, Transliterator_ce_ptr ); + TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; /* change "to" into new object (from "object" ) */ + + utrans = utrans_openInverse( to_orig->utrans, TRANSLITERATOR_ERROR_CODE_P( to ) ); + INTL_CTOR_CHECK_STATUS( to, "transliterator_create_inverse: could not create " + "inverse ICU transliterator" ); + transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC ); + /* no need to close the transliterator manually on construction error */ + INTL_CTOR_CHECK_STATUS( to, "transliterator_create: internal constructor call failed" ); +} +/* }}} */ + +/* {{{ proto array transliterator_list_ids() + * proto array Transliterator::listIDs() + * Return an array with the registered transliterator IDs. + */ +PHP_FUNCTION( transliterator_list_ids ) +{ + UEnumeration *en; + const UChar *elem; + int32_t elem_len; + UErrorCode status = U_ZERO_ERROR; + + intl_error_reset( NULL TSRMLS_CC ); + + if( zend_parse_parameters_none() == FAILURE ) + { + /* seems to be the convention in this lib to return false instead of + * null on bad parameter types, except on constructors and factory + * methods */ + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_list_ids: bad arguments", 0 TSRMLS_CC ); + RETURN_FALSE; + } + + en = utrans_openIDs( &status ); + INTL_CHECK_STATUS( status, + "transliterator_list_ids: Failed to obtain registered transliterators" ); + + array_init( return_value ); + while( (elem = uenum_unext( en, &elem_len, &status )) ) + { + char *el_char = NULL; + int el_len = 0; + + intl_convert_utf16_to_utf8( &el_char, &el_len, elem, elem_len, &status ); + + if( U_FAILURE( status ) ) + { + efree( el_char ); + break; + } + else + { + add_next_index_stringl( return_value, el_char, el_len, 0 ); + } + } + uenum_close( en ); + + intl_error_set_code( NULL, status TSRMLS_CC ); + if( U_FAILURE( status ) ) + { + zval_dtor( return_value ); + RETVAL_FALSE; + intl_error_set_custom_msg( NULL, "transliterator_list_ids: " + "Failed to build array of registered transliterators", 0 TSRMLS_CC ); + } +} +/* }}} */ + +/* {{{ proto string transliterator_transliterate( Transliterator trans, string subject [, int start = 0 [, int end = -1 ]] ) + * proto string Transliterator::transliterate( string subject [, int start = 0 [, int end = -1 ]] ) + * Transliterate a string. */ +PHP_FUNCTION( transliterator_transliterate ) +{ + char *str; + UChar *ustr = NULL, + *uresult = NULL; + int str_len; + int32_t ustr_len = 0, + capacity, + uresult_len; + long start = 0, + limit = -1; + int success = 0, + temp_trans = 0; + TRANSLITERATOR_METHOD_INIT_VARS; + + object = getThis(); + + if( object == NULL ) + { + /* in non-OOP version, accept both a transliterator and a string */ + zval **arg1; + if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "Zs|ll", + &arg1, &str, &str_len, &start, &limit ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_transliterate: bad arguments", 0 TSRMLS_CC ); + RETURN_FALSE; + } + + if( Z_TYPE_PP( arg1 ) == IS_OBJECT && + instanceof_function( Z_OBJCE_PP( arg1 ), Transliterator_ce_ptr TSRMLS_CC ) ) + { + object = *arg1; + } + else + { /* not a transliterator object as first argument */ + int res; + if(Z_TYPE_PP( arg1 ) != IS_STRING ) + { + SEPARATE_ZVAL( arg1 ); + convert_to_string( *arg1 ); + } + ALLOC_INIT_ZVAL( object ); + temp_trans = 1; + res = create_transliterator( Z_STRVAL_PP( arg1 ), Z_STRLEN_PP( arg1 ), + TRANSLITERATOR_FORWARD, object TSRMLS_CC ); + if( res == FAILURE ) + { + char *message = intl_error_get_message( NULL TSRMLS_CC ); + php_error_docref0( NULL TSRMLS_CC, E_WARNING, "Could not create " + "transliterator with ID \"%s\" (%s)", Z_STRVAL_PP( arg1 ), message ); + efree( message ); + /* don't set U_ILLEGAL_ARGUMENT_ERROR to allow fetching of inner error */ + goto cleanup; + } + } + } + else if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", + &str, &str_len, &start, &limit ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_transliterate: bad arguments", 0 TSRMLS_CC ); + RETURN_FALSE; + } + + if( limit < -1 ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_transliterate: \"end\" argument should be " + "either non-negative or -1", 0 TSRMLS_CC ); + RETURN_FALSE; + } + + if( start < 0 || ((limit != -1 ) && (start > limit )) ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_transliterate: \"start\" argument should be " + "non-negative and not bigger than \"end\" (if defined)", 0 TSRMLS_CC ); + RETURN_FALSE; + } + + /* end argument parsing/validation */ + + TRANSLITERATOR_METHOD_FETCH_OBJECT; + + intl_convert_utf8_to_utf16( &ustr, &ustr_len, str, str_len, + TRANSLITERATOR_ERROR_CODE_P( to ) ); + INTL_METHOD_CHECK_STATUS( to, "String conversion of string to UTF-16 failed" ); + + /* we've started allocating resources, goto from now on */ + + if( ( start > ustr_len ) || (( limit != -1 ) && (limit > ustr_len ) ) ) + { + char *msg; + spprintf( &msg, 0, + "transliterator_transliterate: Neither \"start\" nor the \"end\" " + "arguments can exceed the number of UTF-16 code units " + "(in this case, %d)", (int) ustr_len ); + if(msg != NULL ) + { + intl_errors_set( TRANSLITERATOR_ERROR_P( to ), U_ILLEGAL_ARGUMENT_ERROR, + msg, 1 TSRMLS_CC ); + efree( msg ); + } + RETVAL_FALSE; + goto cleanup; + } + + uresult = safe_emalloc( ustr_len, sizeof( UChar ), 1 * sizeof( UChar ) ); + capacity = ustr_len + 1; + + while( 1 ) + { + int32_t temp_limit = ( limit == -1 ? ustr_len : (int32_t) limit ); + memcpy( uresult, ustr, ustr_len * sizeof( UChar ) ); + uresult_len = ustr_len; + + utrans_transUChars( to->utrans, uresult, &uresult_len, capacity, (int32_t) start, + &temp_limit, TRANSLITERATOR_ERROR_CODE_P( to ) ); + if( TRANSLITERATOR_ERROR_CODE( to ) == U_BUFFER_OVERFLOW_ERROR ) + { + efree( uresult ); + + uresult = safe_emalloc( uresult_len, sizeof( UChar ), 1 * sizeof( UChar ) ); + capacity = uresult_len + 1; + + intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC ); + } + else if(TRANSLITERATOR_ERROR_CODE( to ) == U_STRING_NOT_TERMINATED_WARNING ) + { + uresult = safe_erealloc( uresult, uresult_len, sizeof( UChar ), 1 * sizeof( UChar ) ); + + intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC ); + break; + } + else if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) ) + { + intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC ); + intl_errors_set_custom_msg( TRANSLITERATOR_ERROR_P( to ), + "transliterator_transliterate: transliteration failed", 0 TSRMLS_CC ); + goto cleanup; + } + else + break; + } + + uresult[uresult_len] = (UChar) 0; + + success = 1; + +cleanup: + if( ustr ) + efree( ustr ); + + if( success ) { + /* frees uresult even on error */ + INTL_METHOD_RETVAL_UTF8( to, uresult, uresult_len, 1 ); + } + else + { + if( uresult ) + efree( uresult ); + RETVAL_FALSE; + } + + if (temp_trans ) + zval_ptr_dtor( &object ); +} +/* }}} */ + +PHP_METHOD( Transliterator, __construct ) +{ + /* this constructor shouldn't be called as it's private */ + zend_throw_exception( NULL, + "An object of this type cannot be created with the new operator.", + 0 TSRMLS_CC ); +} + +/* {{{ proto int transliterator_get_error_code( Transliterator trans ) + * proto int Transliterator::getErrorCode() + * Get the last error code for this transliterator. + */ +PHP_FUNCTION( transliterator_get_error_code ) +{ + TRANSLITERATOR_METHOD_INIT_VARS + + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Transliterator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_get_error_code: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object (without resetting its last error code ). */ + to = zend_object_store_get_object( object TSRMLS_CC ); + if (to == NULL ) + RETURN_FALSE; + + RETURN_LONG( (long) TRANSLITERATOR_ERROR_CODE( to ) ); +} +/* }}} */ + + +/* {{{ proto string transliterator_get_error_message( Transliterator trans ) + * proto string Transliterator::getErrorMessage() + * Get the last error message for this transliterator. + */ +PHP_FUNCTION( transliterator_get_error_message ) +{ + const char* message = NULL; + TRANSLITERATOR_METHOD_INIT_VARS + + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Transliterator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "transliterator_get_error_message: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + + /* Fetch the object (without resetting its last error code ). */ + to = zend_object_store_get_object( object TSRMLS_CC ); + if (to == NULL ) + RETURN_FALSE; + + /* Return last error message. */ + message = intl_error_get_message( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC ); + RETURN_STRING( message, 0 ); +} +/* }}} */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/ext/intl/transliterator/transliterator_methods.h b/ext/intl/transliterator/transliterator_methods.h new file mode 100644 index 0000000000..b806de84fb --- /dev/null +++ b/ext/intl/transliterator/transliterator_methods.h @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Gustavo Lopes <cataphract@php.net> | + +----------------------------------------------------------------------+ + */ + +#ifndef TRANSLITERATOR_METHODS_H +#define TRANSLITERATOR_METHODS_H + +#include <php.h> + +PHP_FUNCTION( transliterator_create ); + +PHP_FUNCTION( transliterator_create_from_rules ); + +PHP_FUNCTION( transliterator_list_ids ); + +PHP_FUNCTION( transliterator_create_inverse ); + +PHP_FUNCTION( transliterator_transliterate ); + +PHP_METHOD( Transliterator, __construct ); + +PHP_FUNCTION( transliterator_get_error_code ); + +PHP_FUNCTION( transliterator_get_error_message ); + +#endif /* #ifndef TRANSLITERATOR_METHODS_H */ |