summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanislav Malyshev <stas@php.net>2008-07-07 22:51:04 +0000
committerStanislav Malyshev <stas@php.net>2008-07-07 22:51:04 +0000
commit0d16b1516b6b9ef0c2696bc19069e4cda5aee0ea (patch)
treedf4e6a46dea0afafdbc912919b32c2806841a4eb
parent3bab7c18ac205863af3df740144be23c18cf7a72 (diff)
downloadphp-git-0d16b1516b6b9ef0c2696bc19069e4cda5aee0ea.tar.gz
Merge intl extension into core
-rwxr-xr-xext/intl/CREDITS2
-rwxr-xr-xext/intl/TODO5
-rwxr-xr-xext/intl/collator/collator.c96
-rwxr-xr-xext/intl/collator/collator.h29
-rwxr-xr-xext/intl/collator/collator_attr.c157
-rwxr-xr-xext/intl/collator/collator_attr.h28
-rwxr-xr-xext/intl/collator/collator_class.c197
-rwxr-xr-xext/intl/collator/collator_class.h71
-rwxr-xr-xext/intl/collator/collator_compare.c119
-rwxr-xr-xext/intl/collator/collator_compare.h25
-rwxr-xr-xext/intl/collator/collator_convert.c482
-rwxr-xr-xext/intl/collator/collator_convert.h38
-rwxr-xr-xext/intl/collator/collator_create.c135
-rwxr-xr-xext/intl/collator/collator_create.h27
-rwxr-xr-xext/intl/collator/collator_error.c94
-rwxr-xr-xext/intl/collator/collator_error.h26
-rwxr-xr-xext/intl/collator/collator_is_numeric.c305
-rwxr-xr-xext/intl/collator/collator_is_numeric.h26
-rwxr-xr-xext/intl/collator/collator_locale.c71
-rwxr-xr-xext/intl/collator/collator_locale.h25
-rwxr-xr-xext/intl/collator/collator_sort.c532
-rwxr-xr-xext/intl/collator/collator_sort.h29
-rwxr-xr-xext/intl/common/common_error.c266
-rwxr-xr-xext/intl/common/common_error.h30
-rwxr-xr-xext/intl/config.m466
-rwxr-xr-xext/intl/config.w3271
-rwxr-xr-xext/intl/doc/Tutorial.txt239
-rwxr-xr-xext/intl/doc/collator_api.php398
-rwxr-xr-xext/intl/doc/common_api.php58
-rwxr-xr-xext/intl/doc/datefmt_api.php439
-rwxr-xr-xext/intl/doc/formatter_api.php502
-rwxr-xr-xext/intl/doc/grapheme_api.php132
-rwxr-xr-xext/intl/doc/locale_api.php432
-rwxr-xr-xext/intl/doc/msgfmt_api.php209
-rwxr-xr-xext/intl/doc/normalizer_api.php92
-rwxr-xr-xext/intl/formatter/formatter.c152
-rwxr-xr-xext/intl/formatter/formatter.h24
-rwxr-xr-xext/intl/formatter/formatter_attr.c443
-rwxr-xr-xext/intl/formatter/formatter_attr.h32
-rwxr-xr-xext/intl/formatter/formatter_class.c147
-rwxr-xr-xext/intl/formatter/formatter_class.h41
-rwxr-xr-xext/intl/formatter/formatter_data.c72
-rwxr-xr-xext/intl/formatter/formatter_data.h38
-rwxr-xr-xext/intl/formatter/formatter_format.c207
-rwxr-xr-xext/intl/formatter/formatter_format.h31
-rwxr-xr-xext/intl/formatter/formatter_main.c220
-rwxr-xr-xext/intl/formatter/formatter_main.h27
-rwxr-xr-xext/intl/formatter/formatter_parse.c173
-rwxr-xr-xext/intl/formatter/formatter_parse.h25
-rwxr-xr-xext/intl/grapheme/grapheme.h37
-rwxr-xr-xext/intl/grapheme/grapheme_string.c913
-rwxr-xr-xext/intl/grapheme/grapheme_util.c619
-rwxr-xr-xext/intl/grapheme/grapheme_util.h59
-rwxr-xr-xext/intl/intl_common.h44
-rwxr-xr-xext/intl/intl_convert.c154
-rwxr-xr-xext/intl/intl_convert.h33
-rwxr-xr-xext/intl/intl_data.h87
-rwxr-xr-xext/intl/intl_error.c215
-rwxr-xr-xext/intl/intl_error.h46
-rwxr-xr-xext/intl/locale/locale.c70
-rwxr-xr-xext/intl/locale/locale.h36
-rwxr-xr-xext/intl/locale/locale_class.c126
-rwxr-xr-xext/intl/locale/locale_class.h46
-rwxr-xr-xext/intl/locale/locale_methods.c1732
-rwxr-xr-xext/intl/locale/locale_methods.h47
-rwxr-xr-xext/intl/msgformat/msgformat.c256
-rwxr-xr-xext/intl/msgformat/msgformat.h27
-rwxr-xr-xext/intl/msgformat/msgformat_attr.c144
-rwxr-xr-xext/intl/msgformat/msgformat_attr.h26
-rwxr-xr-xext/intl/msgformat/msgformat_class.c127
-rwxr-xr-xext/intl/msgformat/msgformat_class.h41
-rwxr-xr-xext/intl/msgformat/msgformat_data.c98
-rwxr-xr-xext/intl/msgformat/msgformat_data.h41
-rwxr-xr-xext/intl/msgformat/msgformat_format.c184
-rwxr-xr-xext/intl/msgformat/msgformat_format.h25
-rwxr-xr-xext/intl/msgformat/msgformat_helpers.cpp211
-rwxr-xr-xext/intl/msgformat/msgformat_helpers.h25
-rwxr-xr-xext/intl/msgformat/msgformat_parse.c157
-rwxr-xr-xext/intl/msgformat/msgformat_parse.h25
-rwxr-xr-xext/intl/normalizer/normalizer.c68
-rwxr-xr-xext/intl/normalizer/normalizer.h37
-rwxr-xr-xext/intl/normalizer/normalizer_class.c82
-rwxr-xr-xext/intl/normalizer/normalizer_class.h52
-rwxr-xr-xext/intl/normalizer/normalizer_normalize.c256
-rwxr-xr-xext/intl/normalizer/normalizer_normalize.h25
-rwxr-xr-xext/intl/php_intl.c490
-rwxr-xr-xext/intl/php_intl.h76
-rwxr-xr-xext/intl/tests/bug12887.phpt29
-rwxr-xr-xext/intl/tests/collation_customization.phpt70
-rwxr-xr-xext/intl/tests/collator_asort.phpt242
-rwxr-xr-xext/intl/tests/collator_compare.phpt134
-rwxr-xr-xext/intl/tests/collator_create.phpt81
-rwxr-xr-xext/intl/tests/collator_get_error_code.phpt47
-rwxr-xr-xext/intl/tests/collator_get_error_message.phpt36
-rwxr-xr-xext/intl/tests/collator_get_locale.phpt52
-rwxr-xr-xext/intl/tests/collator_get_set_attribute.phpt41
-rwxr-xr-xext/intl/tests/collator_get_set_strength.phpt41
-rwxr-xr-xext/intl/tests/collator_sort.phpt247
-rwxr-xr-xext/intl/tests/collator_sort_with_sort_keys.phpt189
-rwxr-xr-xext/intl/tests/dateformat_format.phpt297
-rwxr-xr-xext/intl/tests/dateformat_format_parse.phpt307
-rwxr-xr-xext/intl/tests/dateformat_get_datetype.phpt57
-rwxr-xr-xext/intl/tests/dateformat_get_locale.phpt53
-rwxr-xr-xext/intl/tests/dateformat_get_set_calendar.phpt60
-rwxr-xr-xext/intl/tests/dateformat_get_set_pattern.phpt84
-rwxr-xr-xext/intl/tests/dateformat_get_timetype.phpt57
-rwxr-xr-xext/intl/tests/dateformat_get_timezone_id.phpt49
-rwxr-xr-xext/intl/tests/dateformat_is_set_lenient.phpt88
-rwxr-xr-xext/intl/tests/dateformat_localtime.phpt133
-rwxr-xr-xext/intl/tests/dateformat_parse.phpt82
-rwxr-xr-xext/intl/tests/dateformat_parse_localtime_parsepos.phpt119
-rwxr-xr-xext/intl/tests/dateformat_parse_timestamp_parsepos.phpt137
-rwxr-xr-xext/intl/tests/dateformat_set_timezone_id.phpt75
-rwxr-xr-xext/intl/tests/formatter_fail.phpt79
-rwxr-xr-xext/intl/tests/formatter_format.phpt108
-rwxr-xr-xext/intl/tests/formatter_format_conv.phpt24
-rwxr-xr-xext/intl/tests/formatter_format_currency.phpt45
-rwxr-xr-xext/intl/tests/formatter_get_error.phpt31
-rwxr-xr-xext/intl/tests/formatter_get_locale.phpt49
-rwxr-xr-xext/intl/tests/formatter_get_set_attribute.phpt193
-rwxr-xr-xext/intl/tests/formatter_get_set_pattern.phpt52
-rwxr-xr-xext/intl/tests/formatter_get_set_symbol.phpt184
-rwxr-xr-xext/intl/tests/formatter_get_set_text_attribute.phpt121
-rwxr-xr-xext/intl/tests/formatter_parse.phpt42
-rwxr-xr-xext/intl/tests/formatter_parse_currency.phpt37
-rwxr-xr-xext/intl/tests/grapheme.phpt1187
-rwxr-xr-xext/intl/tests/intl_error_name.phpt25
-rwxr-xr-xext/intl/tests/intl_get_error_code.phpt24
-rwxr-xr-xext/intl/tests/intl_get_error_message.phpt21
-rwxr-xr-xext/intl/tests/intl_is_failure.phpt25
-rwxr-xr-xext/intl/tests/locale_compose_locale.phpt197
-rwxr-xr-xext/intl/tests/locale_filter_matches.phpt365
-rwxr-xr-xext/intl/tests/locale_get_all_variants.phpt62
-rwxr-xr-xext/intl/tests/locale_get_default.phpt46
-rwxr-xr-xext/intl/tests/locale_get_display_language.phpt280
-rwxr-xr-xext/intl/tests/locale_get_display_name.phpt340
-rwxr-xr-xext/intl/tests/locale_get_display_region.phpt274
-rwxr-xr-xext/intl/tests/locale_get_display_script.phpt274
-rwxr-xr-xext/intl/tests/locale_get_display_variant.phpt274
-rwxr-xr-xext/intl/tests/locale_get_keywords.phpt139
-rwxr-xr-xext/intl/tests/locale_get_primary_language.phpt121
-rwxr-xr-xext/intl/tests/locale_get_region.phpt123
-rwxr-xr-xext/intl/tests/locale_get_script.phpt122
-rwxr-xr-xext/intl/tests/locale_lookup.phpt99
-rwxr-xr-xext/intl/tests/locale_parse_locale.phpt201
-rwxr-xr-xext/intl/tests/locale_set_default.phpt133
-rwxr-xr-xext/intl/tests/msgfmt_fail.phpt101
-rwxr-xr-xext/intl/tests/msgfmt_format.phpt70
-rwxr-xr-xext/intl/tests/msgfmt_get_error.phpt29
-rwxr-xr-xext/intl/tests/msgfmt_get_locale.phpt40
-rwxr-xr-xext/intl/tests/msgfmt_get_set_pattern.phpt53
-rwxr-xr-xext/intl/tests/msgfmt_parse.phpt114
-rwxr-xr-xext/intl/tests/normalizer_normalize.phpt160
-rwxr-xr-xext/intl/tests/regression_sort_and_cow.phpt85
-rwxr-xr-xext/intl/tests/regression_sort_eq.phpt51
-rwxr-xr-xext/intl/tests/regression_sortwsk_and_cow.phpt86
-rwxr-xr-xext/intl/tests/regression_sortwsk_eq.phpt51
-rwxr-xr-xext/intl/tests/ut_common.inc385
158 files changed, 23151 insertions, 0 deletions
diff --git a/ext/intl/CREDITS b/ext/intl/CREDITS
new file mode 100755
index 0000000000..3c54042507
--- /dev/null
+++ b/ext/intl/CREDITS
@@ -0,0 +1,2 @@
+Internationalization
+Ed Batutis, Vladimir Iordanov, Dmitry Lakhtyuk, Stanislav Malyshev, Vadim Savchuk, Kirti Velankar
diff --git a/ext/intl/TODO b/ext/intl/TODO
new file mode 100755
index 0000000000..a388c954e7
--- /dev/null
+++ b/ext/intl/TODO
@@ -0,0 +1,5 @@
+- Document U_* error constants
+- For IntlDateFormatter
+-- Accept and produce DateTime objects in 5.3 and 6
+-- Create convertor from ICU pattern to PHP pattern
+
diff --git a/ext/intl/collator/collator.c b/ext/intl/collator/collator.c
new file mode 100755
index 0000000000..4c1bbc7797
--- /dev/null
+++ b/ext/intl/collator/collator.c
@@ -0,0 +1,96 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "collator_class.h"
+#include "collator.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+
+/* {{{ collator_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void collator_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Collator_ce_ptr )
+ {
+ zend_error( E_ERROR, "Collator class not defined" );
+ return;
+ }
+
+ #define COLLATOR_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define COLLATOR_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( #x ) - 1, UCOL_##x TSRMLS_CC );
+ #define COLLATOR_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ // UColAttributeValue constants
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "DEFAULT_VALUE", UCOL_DEFAULT );
+
+ COLLATOR_EXPOSE_CLASS_CONST( PRIMARY );
+ COLLATOR_EXPOSE_CLASS_CONST( SECONDARY );
+ COLLATOR_EXPOSE_CLASS_CONST( TERTIARY );
+ COLLATOR_EXPOSE_CLASS_CONST( DEFAULT_STRENGTH );
+ COLLATOR_EXPOSE_CLASS_CONST( QUATERNARY );
+ COLLATOR_EXPOSE_CLASS_CONST( IDENTICAL );
+
+ COLLATOR_EXPOSE_CLASS_CONST( OFF );
+ COLLATOR_EXPOSE_CLASS_CONST( ON );
+
+ COLLATOR_EXPOSE_CLASS_CONST( SHIFTED );
+ COLLATOR_EXPOSE_CLASS_CONST( NON_IGNORABLE );
+
+ COLLATOR_EXPOSE_CLASS_CONST( LOWER_FIRST );
+ COLLATOR_EXPOSE_CLASS_CONST( UPPER_FIRST );
+
+ // UColAttribute constants
+ COLLATOR_EXPOSE_CLASS_CONST( FRENCH_COLLATION );
+ COLLATOR_EXPOSE_CLASS_CONST( ALTERNATE_HANDLING );
+ COLLATOR_EXPOSE_CLASS_CONST( CASE_FIRST );
+ COLLATOR_EXPOSE_CLASS_CONST( CASE_LEVEL );
+ COLLATOR_EXPOSE_CLASS_CONST( NORMALIZATION_MODE );
+ COLLATOR_EXPOSE_CLASS_CONST( STRENGTH );
+ COLLATOR_EXPOSE_CLASS_CONST( HIRAGANA_QUATERNARY_MODE );
+ COLLATOR_EXPOSE_CLASS_CONST( NUMERIC_COLLATION );
+
+ // ULocDataLocaleType constants
+ COLLATOR_EXPOSE_CONST( ULOC_ACTUAL_LOCALE );
+ COLLATOR_EXPOSE_CONST( ULOC_VALID_LOCALE );
+
+ // sort flags
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_REGULAR", COLLATOR_SORT_REGULAR );
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_STRING", COLLATOR_SORT_STRING );
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_NUMERIC", COLLATOR_SORT_NUMERIC );
+
+ #undef COLLATOR_EXPOSE_CUSTOM_CLASS_CONST
+ #undef COLLATOR_EXPOSE_CLASS_CONST
+ #undef COLLATOR_EXPOSE_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/collator/collator.h b/ext/intl/collator/collator.h
new file mode 100755
index 0000000000..96e7aa097b
--- /dev/null
+++ b/ext/intl/collator/collator.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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_COLLATOR_H
+#define CCOLLATOR_COLLATOR_H
+
+#include <php.h>
+
+#define COLLATOR_SORT_REGULAR 0
+#define COLLATOR_SORT_STRING 1
+#define COLLATOR_SORT_NUMERIC 2
+
+void collator_register_constants( INIT_FUNC_ARGS );
+
+#endif // COLLATOR_COLLATOR_H
diff --git a/ext/intl/collator/collator_attr.c b/ext/intl/collator/collator_attr.c
new file mode 100755
index 0000000000..b4fe0a4e14
--- /dev/null
+++ b/ext/intl/collator/collator_attr.c
@@ -0,0 +1,157 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_convert.h"
+#include "collator_attr.h"
+
+#include <unicode/ustring.h>
+
+/* {{{ proto int Collator::getAttribute( int $attr )
+ * Get collation attribute value. }}} */
+/* {{{ proto int collator_get_attribute( Collator $coll, int $attr )
+ * Get collation attribute value.
+ */
+PHP_FUNCTION( collator_get_attribute )
+{
+ long attribute, value;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, Collator_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ value = ucol_getAttribute( co->ucoll, attribute, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error getting attribute value" );
+
+ RETURN_LONG( value );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::getAttribute( int $attr )
+ * Get collation attribute value. }}} */
+/* {{{ proto bool collator_set_attribute( Collator $coll, int $attr, int $val )
+ * Set collation attribute.
+ */
+PHP_FUNCTION( collator_set_attribute )
+{
+ long attribute, value;
+ COLLATOR_METHOD_INIT_VARS
+
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
+ &object, Collator_ce_ptr, &attribute, &value ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_set_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ // Set new value for the given attribute.
+ ucol_setAttribute( co->ucoll, attribute, value, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error setting attribute value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int Collator::getStrength()
+ * Returns the current collation strength. }}} */
+/* {{{ proto int collator_get_strength(Collator coll)
+ * Returns the current collation strength.
+ */
+PHP_FUNCTION( collator_get_strength )
+{
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_strength: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ // Get current strength and return it.
+ RETURN_LONG( ucol_getStrength( co->ucoll ) );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::setStrength(int strength)
+ * Set the collation strength. }}} */
+/* {{{ proto bool collator_set_strength(Collator coll, int strength)
+ * Set the collation strength.
+ */
+PHP_FUNCTION( collator_set_strength )
+{
+ long strength;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, Collator_ce_ptr, &strength ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_set_strength: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ // Set given strength.
+ ucol_setStrength( co->ucoll, strength );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_attr.h b/ext/intl/collator/collator_attr.h
new file mode 100755
index 0000000000..85636cc486
--- /dev/null
+++ b/ext/intl/collator/collator_attr.h
@@ -0,0 +1,28 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_ATTR_H
+#define CCOLLATOR_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_attribute );
+PHP_FUNCTION( collator_set_attribute );
+PHP_FUNCTION( collator_get_strength );
+PHP_FUNCTION( collator_set_strength );
+
+#endif // COLLATOR_ATTR_H
diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c
new file mode 100755
index 0000000000..1d75f71f64
--- /dev/null
+++ b/ext/intl/collator/collator_class.c
@@ -0,0 +1,197 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "collator_class.h"
+#include "php_intl.h"
+#include "collator_attr.h"
+#include "collator_compare.h"
+#include "collator_sort.h"
+#include "collator_convert.h"
+#include "collator_locale.h"
+#include "collator_create.h"
+#include "collator_error.h"
+#include "intl_error.h"
+
+#include <unicode/ucol.h>
+
+zend_class_entry *Collator_ce_ptr = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// Auxiliary functions needed by objects of 'Collator' class
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ Collator_objects_dtor */
+static void Collator_objects_dtor(
+ void *object,
+ zend_object_handle handle TSRMLS_DC )
+{
+ zend_objects_destroy_object( object, handle TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ Collator_objects_free */
+void Collator_objects_free( zend_object *object TSRMLS_DC )
+{
+ Collator_object* co = (Collator_object*)object;
+
+ zend_object_std_dtor( &co->zo TSRMLS_CC );
+
+ collator_object_destroy( co TSRMLS_CC );
+
+ efree( co );
+}
+/* }}} */
+
+/* {{{ Collator_object_create */
+zend_object_value Collator_object_create(
+ zend_class_entry *ce TSRMLS_DC )
+{
+ zend_object_value retval;
+ Collator_object* intern;
+
+ intern = ecalloc( 1, sizeof(Collator_object) );
+ intl_error_init( COLLATOR_ERROR_P( intern ) TSRMLS_CC );
+ zend_object_std_init( &intern->zo, ce TSRMLS_CC );
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ Collator_objects_dtor,
+ (zend_objects_free_object_storage_t)Collator_objects_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = zend_get_std_object_handlers();
+
+ return retval;
+}
+/* }}} */
+
+/////////////////////////////////////////////////////////////////////////////
+// 'Collator' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ Collator methods arguments info */
+// NOTE: modifying 'collator_XX_args' do not forget to
+// modify approptiate 'collator_XX_args' for
+// the procedural API.
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_2_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_sort_args, 0, 0, 1 )
+ ZEND_ARG_ARRAY_INFO( 1, arr, 0 )
+ ZEND_ARG_INFO( 0, flags )
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ Collator_class_functions
+ * Every 'Collator' class method has an entry in this table
+ */
+
+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 )
+ PHP_NAMED_FE( sort, ZEND_FN( collator_sort ), collator_sort_args )
+ PHP_NAMED_FE( sortWithSortKeys, ZEND_FN( collator_sort_with_sort_keys ), collator_sort_args )
+ PHP_NAMED_FE( asort, ZEND_FN( collator_asort ), collator_sort_args )
+ PHP_NAMED_FE( getAttribute, ZEND_FN( collator_get_attribute ), collator_1_arg )
+ PHP_NAMED_FE( setAttribute, ZEND_FN( collator_set_attribute ), collator_2_args )
+ PHP_NAMED_FE( getStrength, ZEND_FN( collator_get_strength ), collator_0_args )
+ PHP_NAMED_FE( setStrength, ZEND_FN( collator_set_strength ), collator_1_arg )
+ PHP_NAMED_FE( getLocale, ZEND_FN( collator_get_locale ), collator_1_arg )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( collator_get_error_code ), collator_0_args )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( collator_get_error_message ), collator_0_args )
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ collator_register_Collator_class
+ * Initialize 'Collator' class
+ */
+void collator_register_Collator_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ // Create and register 'Collator' class.
+ INIT_CLASS_ENTRY( ce, "Collator", Collator_class_functions );
+ ce.create_object = Collator_object_create;
+ Collator_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ // Declare 'Collator' class properties.
+ if( !Collator_ce_ptr )
+ {
+ zend_error( E_ERROR,
+ "Collator: attempt to create properties "
+ "on a non-registered class." );
+ return;
+ }
+}
+/* }}} */
+
+/* {{{ void collator_object_init( Collator_object* co )
+ * Initialize internals of Collator_object.
+ * Must be called before any other call to 'collator_object_...' functions.
+ */
+void collator_object_init( Collator_object* co TSRMLS_DC )
+{
+ if( !co )
+ return;
+
+ intl_error_init( COLLATOR_ERROR_P( co ) TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void collator_object_destroy( Collator_object* co )
+ * Clean up mem allocted by internals of Collator_object
+ */
+void collator_object_destroy( Collator_object* co TSRMLS_DC )
+{
+ if( !co )
+ return;
+
+ if( co->ucoll )
+ {
+ ucol_close( co->ucoll );
+ co->ucoll = NULL;
+ }
+
+ intl_error_reset( COLLATOR_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/collator/collator_class.h b/ext/intl/collator/collator_class.h
new file mode 100755
index 0000000000..8d4c9d97f4
--- /dev/null
+++ b/ext/intl/collator/collator_class.h
@@ -0,0 +1,71 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_CLASS_H
+#define COLLATOR_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+
+#include <unicode/ucol.h>
+
+typedef struct {
+ zend_object zo;
+
+ // ICU collator
+ UCollator* ucoll;
+
+ // error handling
+ intl_error err;
+
+} Collator_object;
+
+#define COLLATOR_ERROR(co) (co)->err
+#define COLLATOR_ERROR_P(co) &(COLLATOR_ERROR(co))
+
+#define COLLATOR_ERROR_CODE(co) INTL_ERROR_CODE(COLLATOR_ERROR(co))
+#define COLLATOR_ERROR_CODE_P(co) &(INTL_ERROR_CODE(COLLATOR_ERROR(co)))
+
+void collator_register_Collator_class( TSRMLS_D );
+void collator_object_init( Collator_object* co TSRMLS_DC );
+void collator_object_destroy( Collator_object* co TSRMLS_DC );
+
+extern zend_class_entry *Collator_ce_ptr;
+
+/* Auxiliary macros */
+
+#define COLLATOR_METHOD_INIT_VARS \
+ zval* object = NULL; \
+ Collator_object* co = NULL; \
+ intl_error_reset( NULL TSRMLS_CC ); \
+
+#define COLLATOR_METHOD_FETCH_OBJECT \
+ co = (Collator_object *) zend_object_store_get_object( object TSRMLS_CC ); \
+ intl_error_reset( COLLATOR_ERROR_P( co ) TSRMLS_CC ); \
+
+// Macro to check return value of a ucol_* function call.
+#define COLLATOR_CHECK_STATUS( co, msg ) \
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); \
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) \
+ { \
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), msg, 0 TSRMLS_CC ); \
+ RETURN_FALSE; \
+ } \
+
+#endif // #ifndef COLLATOR_CLASS_H
diff --git a/ext/intl/collator/collator_compare.c b/ext/intl/collator/collator_compare.c
new file mode 100755
index 0000000000..e6029e7920
--- /dev/null
+++ b/ext/intl/collator/collator_compare.c
@@ -0,0 +1,119 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_compare.h"
+#include "intl_convert.h"
+
+/* {{{ proto int Collator::compare( string $str1, string $str2 )
+ * Compare two strings. }}} */
+/* {{{ proto int collator_compare( Collator $coll, string $str1, string $str2 )
+ * Compare two strings.
+ */
+PHP_FUNCTION( collator_compare )
+{
+ char* str1 = NULL;
+ char* str2 = NULL;
+ int str1_len = 0;
+ int str2_len = 0;
+
+ UChar* ustr1 = NULL;
+ UChar* ustr2 = NULL;
+ int ustr1_len = 0;
+ int ustr2_len = 0;
+
+ UCollationResult result;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
+ &object, Collator_ce_ptr, &str1, &str1_len, &str2, &str2_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_compare: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+
+ /*
+ * Compare given strings (converting them to UTF-16 first).
+ */
+
+ // First convert the strings to UTF-16.
+ intl_convert_utf8_to_utf16(
+ &ustr1, &ustr1_len, str1, str1_len, COLLATOR_ERROR_CODE_P( co ) );
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ // Set global error code.
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+
+ // Set error messages.
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Error converting first argument to UTF-16", 0 TSRMLS_CC );
+ efree( ustr1 );
+ RETURN_FALSE;
+ }
+
+ intl_convert_utf8_to_utf16(
+ &ustr2, &ustr2_len, str2, str2_len, COLLATOR_ERROR_CODE_P( co ) );
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ // Set global error code.
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+
+ // Set error messages.
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Error converting second argument to UTF-16", 0 TSRMLS_CC );
+ efree( ustr1 );
+ efree( ustr2 );
+ RETURN_FALSE;
+ }
+
+ // Then compare them.
+ result = ucol_strcoll(
+ co->ucoll,
+ ustr1, ustr1_len,
+ ustr2, ustr2_len );
+
+ if( ustr1 )
+ efree( ustr1 );
+ if( ustr2 )
+ efree( ustr2 );
+
+ // Return result of the comparison.
+ RETURN_LONG( result );
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_compare.h b/ext/intl/collator/collator_compare.h
new file mode 100755
index 0000000000..4e38b79309
--- /dev/null
+++ b/ext/intl/collator/collator_compare.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_COMPARE_H
+#define COLLATOR_COMPARE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_compare );
+
+#endif // COLLATOR_COMPARE_H
diff --git a/ext/intl/collator/collator_convert.c b/ext/intl/collator/collator_convert.c
new file mode 100755
index 0000000000..7bb48ebb4b
--- /dev/null
+++ b/ext/intl/collator/collator_convert.c
@@ -0,0 +1,482 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_is_numeric.h"
+#include "collator_convert.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+#include <php.h>
+
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
+#define CAST_OBJECT_SHOULD_FREE ,0
+#else
+#define CAST_OBJECT_SHOULD_FREE
+#endif
+
+#define COLLATOR_CONVERT_RETURN_FAILED(retval) { \
+ zval_add_ref( &retval ); \
+ return retval; \
+ }
+
+/* {{{ collator_convert_hash_item_from_utf8_to_utf16 */
+static void collator_convert_hash_item_from_utf8_to_utf16(
+ HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex,
+ UErrorCode* status )
+{
+ const char* old_val;
+ int old_val_len;
+ UChar* new_val = NULL;
+ int new_val_len = 0;
+ zval** hashData = NULL;
+ zval* znew_val = NULL;
+
+ // Get current hash item.
+ zend_hash_get_current_data( hash, (void**) &hashData );
+
+ // Process string values only.
+ if( Z_TYPE_P( *hashData ) != IS_STRING )
+ return;
+
+ old_val = Z_STRVAL_P( *hashData );
+ old_val_len = Z_STRLEN_P( *hashData );
+
+ // Convert it from UTF-8 to UTF-16LE and save the result to new_val[_len].
+ intl_convert_utf8_to_utf16( &new_val, &new_val_len, old_val, old_val_len, status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ // Update current hash item with the converted value.
+ MAKE_STD_ZVAL( znew_val );
+ ZVAL_STRINGL( znew_val, (char*)new_val, UBYTES(new_val_len), FALSE );
+
+ if( hashKeyType == HASH_KEY_IS_STRING )
+ {
+ zend_hash_update( hash, hashKey, strlen( hashKey ) + 1,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+ else // hashKeyType == HASH_KEY_IS_LONG
+ {
+ zend_hash_index_update( hash, hashIndex,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_item_from_utf16_to_utf8 */
+static void collator_convert_hash_item_from_utf16_to_utf8(
+ HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex,
+ UErrorCode* status )
+{
+ const char* old_val;
+ int old_val_len;
+ char* new_val = NULL;
+ int new_val_len = 0;
+ zval** hashData = NULL;
+ zval* znew_val = NULL;
+
+ // Get current hash item.
+ zend_hash_get_current_data( hash, (void**) &hashData );
+
+ // Process string values only.
+ if( Z_TYPE_P( *hashData ) != IS_STRING )
+ return;
+
+ old_val = Z_STRVAL_P( *hashData );
+ old_val_len = Z_STRLEN_P( *hashData );
+
+ // Convert it from UTF-16LE to UTF-8 and save the result to new_val[_len].
+ intl_convert_utf16_to_utf8( &new_val, &new_val_len,
+ (UChar*)old_val, UCHARS(old_val_len), status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ // Update current hash item with the converted value.
+ MAKE_STD_ZVAL( znew_val );
+ ZVAL_STRINGL( znew_val, (char*)new_val, new_val_len, FALSE );
+
+ if( hashKeyType == HASH_KEY_IS_STRING )
+ {
+ zend_hash_update( hash, hashKey, strlen( hashKey ) + 1,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+ else // hashKeyType == HASH_KEY_IS_LONG
+ {
+ zend_hash_index_update( hash, hashIndex,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_from_utf8_to_utf16
+ * Convert values of the given hash from UTF-8 encoding to UTF-16LE.
+ */
+void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status )
+{
+ ulong hashIndex = 0;
+ char* hashKey = NULL;
+ int hashKeyType = 0;
+
+ zend_hash_internal_pointer_reset( hash );
+ while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) )
+ != HASH_KEY_NON_EXISTANT )
+ {
+ // Convert current hash item from UTF-8 to UTF-16LE.
+ collator_convert_hash_item_from_utf8_to_utf16(
+ hash, hashKeyType, hashKey, hashIndex, status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ // Proceed to the next item.
+ zend_hash_move_forward( hash );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_from_utf16_to_utf8
+ * Convert values of the given hash from UTF-16LE encoding to UTF-8.
+ */
+void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status )
+{
+ ulong hashIndex = 0;
+ char* hashKey = NULL;
+ int hashKeyType = 0;
+
+ zend_hash_internal_pointer_reset( hash );
+ while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) )
+ != HASH_KEY_NON_EXISTANT )
+ {
+ // Convert current hash item from UTF-16LE to UTF-8.
+ collator_convert_hash_item_from_utf16_to_utf8(
+ hash, hashKeyType, hashKey, hashIndex, status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ // Proceed to the next item.
+ zend_hash_move_forward( hash );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_zstr_utf16_to_utf8
+ *
+ * Convert string from utf16 to utf8.
+ *
+ * @param zval* utf16_zval String to convert.
+ *
+ * @return zval* Converted string.
+ */
+zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval )
+{
+ zval* utf8_zval = NULL;
+ char* str = NULL;
+ int str_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Convert to utf8 then.
+ intl_convert_utf16_to_utf8( &str, &str_len,
+ (UChar*) Z_STRVAL_P(utf16_zval), UCHARS( Z_STRLEN_P(utf16_zval) ), &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error converting utf16 to utf8 in collator_convert_zval_utf16_to_utf8()" );
+
+ ALLOC_INIT_ZVAL( utf8_zval );
+ ZVAL_STRINGL( utf8_zval, str, str_len, FALSE );
+
+ return utf8_zval;
+}
+/* }}} */
+
+/* {{{ collator_convert_zstr_utf8_to_utf16
+ *
+ * Convert string from utf8 to utf16.
+ *
+ * @param zval* utf8_zval String to convert.
+ *
+ * @return zval* Converted string.
+ */
+zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval )
+{
+ zval* zstr = NULL;
+ UChar* ustr = NULL;
+ int ustr_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Convert the string to UTF-16.
+ intl_convert_utf8_to_utf16(
+ &ustr, &ustr_len,
+ Z_STRVAL_P( utf8_zval ), Z_STRLEN_P( utf8_zval ),
+ &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error casting object to string in collator_convert_zstr_utf8_to_utf16()" );
+
+ // Set string.
+ ALLOC_INIT_ZVAL( zstr );
+ ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE );
+
+ return zstr;
+}
+/* }}} */
+
+/* {{{ collator_convert_object_to_string
+ * Convert object to UTF16-encoded string.
+ */
+zval* collator_convert_object_to_string( zval* obj TSRMLS_DC )
+{
+ zval* zstr = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ UChar* ustr = NULL;
+ int ustr_len = 0;
+
+ // Bail out if it's not an object.
+ if( Z_TYPE_P( obj ) != IS_OBJECT )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+
+ // Try object's handlers.
+ if( Z_OBJ_HT_P(obj)->get )
+ {
+ zstr = Z_OBJ_HT_P(obj)->get( obj TSRMLS_CC );
+
+ switch( Z_TYPE_P( zstr ) )
+ {
+ case IS_OBJECT:
+ {
+ // Bail out.
+ zval_ptr_dtor( &zstr );
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ } break;
+
+ case IS_STRING:
+ break;
+
+ default:
+ {
+ convert_to_string( zstr );
+ } break;
+ }
+ }
+ else if( Z_OBJ_HT_P(obj)->cast_object )
+ {
+ ALLOC_INIT_ZVAL( zstr );
+
+ if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING CAST_OBJECT_SHOULD_FREE TSRMLS_CC ) == FAILURE )
+ {
+ // cast_object failed => bail out.
+ zval_ptr_dtor( &zstr );
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+ }
+
+ // Object wasn't successfuly converted => bail out.
+ if( zstr == NULL )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+
+ // Convert the string to UTF-16.
+ intl_convert_utf8_to_utf16(
+ &ustr, &ustr_len,
+ Z_STRVAL_P( zstr ), Z_STRLEN_P( zstr ),
+ &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error casting object to string in collator_convert_object_to_string()" );
+
+ // Cleanup zstr to hold utf16 string.
+ zval_dtor( zstr );
+
+ // Set string.
+ ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE );
+
+ // Don't free ustr cause it's set in zstr without copy.
+ // efree( ustr );
+
+ return zstr;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_number
+ *
+ * Convert string to number.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number. If str is not numeric string return number zero.
+ */
+zval* collator_convert_string_to_number( zval* str )
+{
+ zval* num = collator_convert_string_to_number_if_possible( str );
+ if( num == str )
+ {
+ // String wasn't converted => return zero.
+ zval_ptr_dtor( &num );
+
+ ALLOC_INIT_ZVAL( num );
+ ZVAL_LONG( num, 0 );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_double
+ *
+ * Convert string to double.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number. If str is not numeric string return number zero.
+ */
+zval* collator_convert_string_to_double( zval* str )
+{
+ zval* num = collator_convert_string_to_number( str );
+ if( Z_TYPE_P(num) == IS_LONG )
+ {
+ ZVAL_DOUBLE( num, Z_LVAL_P( num ) );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_number_if_possible
+ *
+ * Convert string to numer.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number if str is numeric string. Otherwise
+ * original str param.
+ */
+zval* collator_convert_string_to_number_if_possible( zval* str )
+{
+ zval* num = NULL;
+ int is_numeric = 0;
+ long lval = 0;
+ double dval = 0;
+
+ if( Z_TYPE_P( str ) != IS_STRING )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( str );
+ }
+
+ if( ( is_numeric = collator_is_numeric( (UChar*) Z_STRVAL_P(str), UCHARS( Z_STRLEN_P(str) ), &lval, &dval, 1 ) ) )
+ {
+ ALLOC_INIT_ZVAL( num );
+
+ if( is_numeric == IS_LONG )
+ Z_LVAL_P(num) = lval;
+ if( is_numeric == IS_DOUBLE )
+ Z_DVAL_P(num) = dval;
+
+ Z_TYPE_P(num) = is_numeric;
+ }
+ else
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( str );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_make_printable_zval
+ *
+ * Returns string from input zval.
+ *
+ * @param zval* arg zval to get string from
+ *
+ * @return zval* UTF16 string.
+ */
+zval* collator_make_printable_zval( zval* arg )
+{
+ zval arg_copy;
+ int use_copy = 0;
+ zval* str = NULL;
+
+ if( Z_TYPE_P(arg) != IS_STRING )
+ {
+ zend_make_printable_zval(arg, &arg_copy, &use_copy);
+
+ if( use_copy )
+ {
+ str = collator_convert_zstr_utf8_to_utf16( &arg_copy );
+ zval_dtor( &arg_copy );
+ }
+ else
+ {
+ str = collator_convert_zstr_utf8_to_utf16( arg );
+ }
+ }
+ else
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( arg );
+ }
+
+ return str;
+}
+/* }}} */
+
+/* {{{ collator_normalize_sort_argument
+ *
+ * Normalize argument to use in sort's compare function.
+ *
+ * @param zval* arg Sort's argument to normalize.
+ *
+ * @return zval* Normalized copy of arg or unmodified arg
+ * if normalization is not needed.
+ */
+zval* collator_normalize_sort_argument( zval* arg )
+{
+ zval* n_arg = NULL;
+
+ if( Z_TYPE_P( arg ) != IS_STRING )
+ {
+ // If its not a string then nothing to do.
+ // Return original arg.
+ COLLATOR_CONVERT_RETURN_FAILED( arg );
+ }
+
+ // Try convert to number.
+ n_arg = collator_convert_string_to_number_if_possible( arg );
+
+ if( n_arg == arg )
+ {
+ // Conversion to number failed.
+ zval_ptr_dtor( &n_arg );
+
+ // Convert string to utf8.
+ n_arg = collator_convert_zstr_utf16_to_utf8( arg );
+ }
+
+ return n_arg;
+}
+/* }}} */
+/*
+ * 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/collator/collator_convert.h b/ext/intl/collator/collator_convert.h
new file mode 100755
index 0000000000..8322ea998b
--- /dev/null
+++ b/ext/intl/collator/collator_convert.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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_CONVERT_H
+#define COLLATOR_CONVERT_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+
+void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status );
+void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status );
+
+zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval );
+zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval );
+
+zval* collator_normalize_sort_argument( zval* arg );
+zval* collator_convert_object_to_string( zval* obj TSRMLS_DC );
+zval* collator_convert_string_to_number( zval* arg );
+zval* collator_convert_string_to_number_if_possible( zval* str );
+zval* collator_convert_string_to_double( zval* str );
+
+zval* collator_make_printable_zval( zval* arg );
+
+#endif // COLLATOR_CONVERT_H
diff --git a/ext/intl/collator/collator_create.c b/ext/intl/collator/collator_create.c
new file mode 100755
index 0000000000..6fbb3da8b3
--- /dev/null
+++ b/ext/intl/collator/collator_create.c
@@ -0,0 +1,135 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_create.h"
+#include "intl_data.h"
+
+/* {{{ proto Collator collator_create( string $locale )
+ * Create collator.
+ */
+PHP_FUNCTION( collator_create )
+{
+ char* locale;
+ int locale_len = 0;
+ zval* object;
+ Collator_object* co;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &locale, &locale_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_create: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN(locale_len);
+ // Create a Collator object and save the ICU collator into it.
+ if( ( object = getThis() ) == NULL )
+ object = return_value;
+
+ if( Z_TYPE_P( object ) != IS_OBJECT )
+ object_init_ex( object, Collator_ce_ptr );
+
+ co = (Collator_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ intl_error_reset( COLLATOR_ERROR_P( co ) TSRMLS_CC );
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ // Open ICU collator.
+ co->ucoll = ucol_open( locale, COLLATOR_ERROR_CODE_P( co ) );
+
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) || co->ucoll == NULL )
+ {
+ intl_error_set( NULL, COLLATOR_ERROR_CODE( co ),
+ "collator_create: unable to open ICU collator", 0 TSRMLS_CC );
+
+ // Collator creation failed.
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto Collator Collator::__construct( string $locale )
+ * Collator object constructor.
+ */
+PHP_METHOD( Collator, __construct )
+{
+ char* locale = NULL;
+ int locale_len = 0;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ object = getThis();
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &locale, &locale_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "__construct: unable to parse input params", 0 TSRMLS_CC );
+
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, object);
+ /* Fetch the object. */
+ co = (Collator_object*) zend_object_store_get_object( object TSRMLS_CC );
+
+ intl_error_reset( COLLATOR_ERROR_P( co ) TSRMLS_CC );
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ // Open ICU collator.
+ co->ucoll = ucol_open( locale, COLLATOR_ERROR_CODE_P( co ) );
+
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) || co->ucoll == NULL )
+ {
+ intl_error_set( NULL, COLLATOR_ERROR_CODE( co ),
+ "__construct: unable to open ICU collator", 0 TSRMLS_CC );
+
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_create.h b/ext/intl/collator/collator_create.h
new file mode 100755
index 0000000000..b740e82d68
--- /dev/null
+++ b/ext/intl/collator/collator_create.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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_CREATE_H
+#define COLLATOR_CREATE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_create );
+
+PHP_METHOD( Collator, __construct );
+
+#endif // COLLATOR_CREATE_H
diff --git a/ext/intl/collator/collator_error.c b/ext/intl/collator/collator_error.c
new file mode 100755
index 0000000000..55e366ccba
--- /dev/null
+++ b/ext/intl/collator/collator_error.c
@@ -0,0 +1,94 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_error.h"
+
+/* {{{ proto int Collator::getErrorCode( Collator $coll )
+ * Get collator's last error code. }}} */
+/* {{{ proto int collator_get_error_code( Collator $coll )
+ * Get collator's last error code.
+ */
+PHP_FUNCTION( collator_get_error_code )
+{
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object (without resetting its last error code).
+ co = (Collator_object *) zend_object_store_get_object(object TSRMLS_CC);
+ if( co == NULL )
+ RETURN_FALSE;
+
+ // Return collator's last error code.
+ RETURN_LONG( COLLATOR_ERROR_CODE( co ) );
+}
+/* }}} */
+
+/* {{{ proto string Collator::getErrorMessage( Collator $coll )
+ * Get text description for collator's last error code. }}} */
+/* {{{ proto string collator_get_error_message( Collator $coll )
+ * Get text description for collator's last error code.
+ */
+PHP_FUNCTION( collator_get_error_message )
+{
+ const char* message = NULL;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object (without resetting its last error code).
+ co = (Collator_object *) zend_object_store_get_object( object TSRMLS_CC );
+ if( co == NULL )
+ RETURN_FALSE;
+
+ // Return last error message.
+ message = intl_error_get_message( COLLATOR_ERROR_P( co ) TSRMLS_CC );
+ RETURN_STRING( (char*)message, FALSE );
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_error.h b/ext/intl/collator/collator_error.h
new file mode 100755
index 0000000000..b2f44ea2a3
--- /dev/null
+++ b/ext/intl/collator/collator_error.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_ERROR_H
+#define COLLATOR_ERROR_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_error_code );
+PHP_FUNCTION( collator_get_error_message );
+
+#endif // COLLATOR_ERROR_H
diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c
new file mode 100755
index 0000000000..a8abfac12b
--- /dev/null
+++ b/ext/intl/collator/collator_is_numeric.c
@@ -0,0 +1,305 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "collator_is_numeric.h"
+
+#if ZEND_MODULE_API_NO < 20071006
+/* not 5.3 */
+#ifndef ALLOCA_FLAG
+#define ALLOCA_FLAG(use_heap)
+#endif
+#define _do_alloca(x, y) do_alloca((x))
+#define _free_alloca(x, y) free_alloca((x))
+#else
+#define _do_alloca do_alloca
+#define _free_alloca free_alloca
+#endif
+/* {{{ collator_u_strtod
+ * Taken from PHP6:zend_u_strtod()
+ */
+static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
+{
+ const UChar *u = nptr, *nstart;
+ UChar c = *u;
+ int any = 0;
+ ALLOCA_FLAG(use_heap);
+
+ while (u_isspace(c)) {
+ c = *++u;
+ }
+ nstart = u;
+
+ if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
+ c = *++u;
+ }
+
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any = 1;
+ c = *++u;
+ }
+
+ if (c == 0x2E /*'.'*/) {
+ c = *++u;
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any = 1;
+ c = *++u;
+ }
+ }
+
+ if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) {
+ const UChar *e = u;
+ int any_exp = 0;
+
+ c = *++u;
+ if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
+ c = *++u;
+ }
+
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any_exp = 1;
+ c = *++u;
+ }
+
+ if (!any_exp) {
+ u = e;
+ }
+ }
+
+ if (any) {
+ char buf[64], *numbuf, *bufpos;
+ int length = u - nstart;
+ double value;
+
+ if (length < sizeof(buf)) {
+ numbuf = buf;
+ } else {
+ numbuf = (char *) _do_alloca(length + 1, use_heap);
+ }
+
+ bufpos = numbuf;
+
+ while (nstart < u) {
+ *bufpos++ = (char) *nstart++;
+ }
+
+ *bufpos = '\0';
+ value = zend_strtod(numbuf, NULL);
+
+ if (numbuf != buf) {
+ _free_alloca(numbuf, use_heap);
+ }
+
+ if (endptr != NULL) {
+ *endptr = (UChar *)u;
+ }
+
+ return value;
+ }
+
+ if (endptr != NULL) {
+ *endptr = (UChar *)nptr;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ collator_u_strtol
+ * Taken from PHP6:zend_u_strtol()
+ *
+ * Convert a Unicode string to a long integer.
+ *
+ * Ignores `locale' stuff.
+ */
+static long collator_u_strtol(nptr, endptr, base)
+ const UChar *nptr;
+ UChar **endptr;
+ register int base;
+{
+ register const UChar *s = nptr;
+ register unsigned long acc;
+ register UChar c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ if (s == NULL) {
+ errno = ERANGE;
+ if (endptr != NULL) {
+ *endptr = NULL;
+ }
+ return 0;
+ }
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (u_isspace(c));
+ if (c == 0x2D /*'-'*/) {
+ neg = 1;
+ c = *s++;
+ } else if (c == 0x2B /*'+'*/)
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ (c == 0x30 /*'0'*/)
+ && (*s == 0x78 /*'x'*/ || *s == 0x58 /*'X'*/)) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = (c == 0x30 /*'0'*/) ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/)
+ c -= 0x30 /*'0'*/;
+ else if (c >= 0x41 /*'A'*/ && c <= 0x5A /*'Z'*/)
+ c -= 0x41 /*'A'*/ - 10;
+ else if (c >= 0x61 /*'a'*/ && c <= 0x7A /*'z'*/)
+ c -= 0x61 /*'a'*/ - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (UChar *)(any ? s - 1 : nptr);
+ return (acc);
+}
+/* }}} */
+
+
+/* {{{ collator_is_numeric]
+ * Taken from PHP6:is_numeric_unicode()
+ */
+zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors )
+{
+ long local_lval;
+ double local_dval;
+ UChar *end_ptr_long, *end_ptr_double;
+ int conv_base=10;
+
+ if (!length) {
+ return 0;
+ }
+
+ /* handle hex numbers */
+ if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
+ conv_base=16;
+ }
+
+ errno=0;
+ local_lval = collator_u_strtol(str, &end_ptr_long, conv_base);
+ if (errno != ERANGE) {
+ if (end_ptr_long == str+length) { /* integer string */
+ if (lval) {
+ *lval = local_lval;
+ }
+ return IS_LONG;
+ } else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
+ return 0;
+ }
+ } else {
+ end_ptr_long = NULL;
+ }
+
+ if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */
+ /* UTODO: keep compatibility with is_numeric_string() here? */
+ return 0;
+ }
+
+ local_dval = collator_u_strtod(str, &end_ptr_double);
+ if (local_dval == 0 && end_ptr_double == str) {
+ end_ptr_double = NULL;
+ } else {
+ if (end_ptr_double == str+length) { /* floating point string */
+ if (!zend_finite(local_dval)) {
+ /* "inf","nan" and maybe other weird ones */
+ return 0;
+ }
+
+ if (dval) {
+ *dval = local_dval;
+ }
+ return IS_DOUBLE;
+ }
+ }
+
+ if (!allow_errors) {
+ return 0;
+ }
+ if (allow_errors == -1) {
+ zend_error(E_NOTICE, "A non well formed numeric value encountered");
+ }
+
+ if (allow_errors) {
+ if (end_ptr_double > end_ptr_long && dval) {
+ *dval = local_dval;
+ return IS_DOUBLE;
+ } else if (end_ptr_long && lval) {
+ *lval = local_lval;
+ return IS_LONG;
+ }
+ }
+ return 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/collator/collator_is_numeric.h b/ext/intl/collator/collator_is_numeric.h
new file mode 100755
index 0000000000..585d58917a
--- /dev/null
+++ b/ext/intl/collator/collator_is_numeric.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_IS_NUMERIC_H
+#define COLLATOR_IS_NUMERIC_H
+
+#include <php.h>
+#include <unicode/uchar.h>
+
+zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors );
+
+#endif // COLLATOR_IS_NUMERIC_H
diff --git a/ext/intl/collator/collator_locale.c b/ext/intl/collator/collator_locale.c
new file mode 100755
index 0000000000..838c50be80
--- /dev/null
+++ b/ext/intl/collator/collator_locale.c
@@ -0,0 +1,71 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_locale.h"
+#include "intl_convert.h"
+
+#include <zend_API.h>
+
+/* {{{ proto string Collator::getLocale( int $type )
+ * Gets the locale name of the collator. }}} */
+/* {{{ proto string collator_get_locale( Collator $coll, int $type )
+ * Gets the locale name of the collator.
+ */
+PHP_FUNCTION( collator_get_locale )
+{
+ int type = 0;
+ char* locale_name = NULL;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, Collator_ce_ptr, &type ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ // Get locale by specified type.
+ locale_name = (char*) ucol_getLocaleByType(
+ co->ucoll, type, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error getting locale by type" );
+
+ // Return it.
+ RETVAL_STRINGL( locale_name, strlen(locale_name), TRUE );
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_locale.h b/ext/intl/collator/collator_locale.h
new file mode 100755
index 0000000000..bda90cd3b9
--- /dev/null
+++ b/ext/intl/collator/collator_locale.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_LOCALE_H
+#define COLLATOR_LOCALE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_locale );
+
+#endif // COLLATOR_LOCALE_H
diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c
new file mode 100755
index 0000000000..db014428ad
--- /dev/null
+++ b/ext/intl/collator/collator_sort.c
@@ -0,0 +1,532 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator.h"
+#include "collator_class.h"
+#include "collator_sort.h"
+#include "collator_convert.h"
+#include "intl_convert.h"
+
+#if !defined(HAVE_PTRDIFF_T) && !defined(_PTRDIFF_T_DEFINED)
+typedef long ptrdiff_t;
+#endif
+
+/**
+ * Declare 'index' which will point to sort key in sort key
+ * buffer.
+ */
+typedef struct _collator_sort_key_index {
+ char* key; // pointer to sort key
+ zval** zstr; // pointer to original string(hash-item)
+} collator_sort_key_index_t;
+
+ZEND_EXTERN_MODULE_GLOBALS( intl )
+
+static const size_t DEF_SORT_KEYS_BUF_SIZE = 1048576;
+static const size_t DEF_SORT_KEYS_BUF_INCREMENT = 1048576;
+
+static const size_t DEF_SORT_KEYS_INDX_BUF_SIZE = 1048576;
+static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576;
+
+static const size_t DEF_UTF16_BUF_SIZE = 1024;
+
+/* {{{ collator_regular_compare_function */
+static int collator_regular_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ Collator_object* co = NULL;
+
+ int rc = SUCCESS;
+
+ zval* str1 = collator_convert_object_to_string( op1 TSRMLS_CC );
+ zval* str2 = collator_convert_object_to_string( op2 TSRMLS_CC );
+
+ zval* num1 = NULL;
+ zval* num2 = NULL;
+ zval* norm1 = NULL;
+ zval* norm2 = NULL;
+
+ // If both args are strings AND either of args is not numeric string
+ // then use ICU-compare. Otherwise PHP-compare.
+ if( Z_TYPE_P(str1) == IS_STRING && Z_TYPE_P(str2) == IS_STRING &&
+ ( str1 == ( num1 = collator_convert_string_to_number_if_possible( str1 ) ) ||
+ str2 == ( num2 = collator_convert_string_to_number_if_possible( str2 ) ) ) )
+ {
+ // Fetch collator object.
+ co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC );
+
+ // Compare the strings using ICU.
+ result->value.lval = ucol_strcoll(
+ co->ucoll,
+ INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1),
+ INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) );
+ result->type = IS_LONG;
+ }
+ else
+ {
+ // num1 is set if str1 and str2 are strings.
+ if( num1 )
+ {
+ if( num1 == str1 )
+ {
+ // str1 is string but not numeric string
+ // just convert it to utf8.
+ norm1 = collator_convert_zstr_utf16_to_utf8( str1 );
+
+ // num2 is not set but str2 is string => do normalization.
+ norm2 = collator_normalize_sort_argument( str2 );
+ }
+ else
+ {
+ // str1 is numeric strings => passthru to PHP-compare.
+ zval_add_ref( &num1 );
+ norm1 = num1;
+
+ // str2 is numeric strings => passthru to PHP-compare.
+ zval_add_ref( &num2 );
+ norm2 = num2;
+ }
+ }
+ else
+ {
+ // num1 is not set if str1 or str2 is not a string => do normalization.
+ norm1 = collator_normalize_sort_argument( str1 );
+
+ // if num1 is not set then num2 is not set as well => do normalization.
+ norm2 = collator_normalize_sort_argument( str2 );
+ }
+
+ rc = compare_function( result, norm1, norm2 TSRMLS_CC );
+
+ zval_ptr_dtor( &norm1 );
+ zval_ptr_dtor( &norm2 );
+ }
+
+ if( num1 )
+ zval_ptr_dtor( &num1 );
+
+ if( num2 )
+ zval_ptr_dtor( &num2 );
+
+ zval_ptr_dtor( &str1 );
+ zval_ptr_dtor( &str2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_numeric_compare_function
+ * Convert input args to double and compare it.
+ */
+static int collator_numeric_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ int rc = SUCCESS;
+ zval* num1 = NULL;
+ zval* num2 = NULL;
+
+ if( Z_TYPE_P(op1) == IS_STRING )
+ {
+ num1 = collator_convert_string_to_double( op1 );
+ op1 = num1;
+ }
+
+ if( Z_TYPE_P(op2) == IS_STRING )
+ {
+ num2 = collator_convert_string_to_double( op2 );
+ op2 = num2;
+ }
+
+ rc = numeric_compare_function( result, op1, op2 TSRMLS_CC);
+
+ if( num1 )
+ zval_ptr_dtor( &num1 );
+ if( num2 )
+ zval_ptr_dtor( &num2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_icu_compare_function
+ * Direct use of ucol_strcoll.
+*/
+static int collator_icu_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ int rc = SUCCESS;
+ Collator_object* co = NULL;
+ zval* str1 = NULL;
+ zval* str2 = NULL;
+
+ str1 = collator_make_printable_zval( op1 );
+ str2 = collator_make_printable_zval( op2 );
+
+ // Fetch collator object.
+ co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC );
+
+ // Compare the strings using ICU.
+ result->value.lval = ucol_strcoll(
+ co->ucoll,
+ INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1),
+ INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) );
+ result->type = IS_LONG;
+
+ zval_ptr_dtor( &str1 );
+ zval_ptr_dtor( &str2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_compare_func
+ * Taken from PHP5 source (array_data_compare).
+ */
+static int collator_compare_func( const void* a, const void* b TSRMLS_DC )
+{
+ Bucket *f;
+ Bucket *s;
+ zval result;
+ zval *first;
+ zval *second;
+
+ f = *((Bucket **) a);
+ s = *((Bucket **) b);
+
+ first = *((zval **) f->pData);
+ second = *((zval **) s->pData);
+
+ if( INTL_G(compare_func)( &result, first, second TSRMLS_CC) == FAILURE )
+ return 0;
+
+ if( Z_TYPE(result) == IS_DOUBLE )
+ {
+ if( Z_DVAL(result) < 0 )
+ return -1;
+ else if( Z_DVAL(result) > 0 )
+ return 1;
+ else
+ return 0;
+ }
+
+ convert_to_long(&result);
+
+ if( Z_LVAL(result) < 0 )
+ return -1;
+ else if( Z_LVAL(result) > 0 )
+ return 1;
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ collator_cmp_sort_keys
+ * Compare sort keys
+ */
+static int collator_cmp_sort_keys( const void *p1, const void *p2 TSRMLS_DC )
+{
+ char* key1 = ((collator_sort_key_index_t*)p1)->key;
+ char* key2 = ((collator_sort_key_index_t*)p2)->key;
+
+ return strcmp( key1, key2 );
+}
+/* }}} */
+
+/* {{{ collator_get_compare_function
+ * Choose compare function according to sort flags.
+ */
+static collator_compare_func_t collator_get_compare_function( const long sort_flags )
+{
+ collator_compare_func_t func;
+
+ switch( sort_flags )
+ {
+ case COLLATOR_SORT_NUMERIC:
+ func = collator_numeric_compare_function;
+ break;
+
+ case COLLATOR_SORT_STRING:
+ func = collator_icu_compare_function;
+ break;
+
+ case COLLATOR_SORT_REGULAR:
+ default:
+ func = collator_regular_compare_function;
+ break;
+ }
+
+ return func;
+}
+/* }}} */
+
+/* {{{ collator_sort_internal
+ * Common code shared by collator_sort() and collator_asort() API functions.
+ */
+static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
+{
+ zval* array = NULL;
+ HashTable* hash = NULL;
+ zval* saved_collator = NULL;
+ long sort_flags = COLLATOR_SORT_REGULAR;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa|l",
+ &object, Collator_ce_ptr, &array, &sort_flags ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_sort_internal: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ // Set 'compare function' according to sort flags.
+ INTL_G(compare_func) = collator_get_compare_function( sort_flags );
+
+ hash = HASH_OF( array );
+
+ // Convert strings in the specified array from UTF-8 to UTF-16.
+ collator_convert_hash_from_utf8_to_utf16( hash, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" );
+
+ // Save specified collator in the request-global (?) variable.
+ saved_collator = INTL_G( current_collator );
+ INTL_G( current_collator ) = object;
+
+ // Sort specified array.
+ zend_hash_sort( hash, zend_qsort, collator_compare_func, renumber TSRMLS_CC );
+
+ // Restore saved collator.
+ INTL_G( current_collator ) = saved_collator;
+
+ // Convert strings in the specified array back to UTF-8.
+ collator_convert_hash_from_utf16_to_utf8( hash, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-16 to UTF-8" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool Collator::sort( Collator $coll, array(string) $arr [, int $sort_flags] )
+ * Sort array using specified collator. }}} */
+/* {{{ proto bool collator_sort( Collator $coll, array(string) $arr [, int $sort_flags] )
+ * Sort array using specified collator.
+ */
+PHP_FUNCTION( collator_sort )
+{
+ collator_sort_internal( TRUE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::sortWithSortKeys( Collator $coll, array(string) $arr )
+ * Equivalent to standard PHP sort using Collator.
+ * Uses ICU ucol_getSortKey for performance. }}} */
+/* {{{ proto bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr )
+ * Equivalent to standard PHP sort using Collator.
+ * Uses ICU ucol_getSortKey for performance.
+ */
+PHP_FUNCTION( collator_sort_with_sort_keys )
+{
+ zval* array = NULL;
+ HashTable* hash = NULL;
+ zval** hashData = NULL; // currently processed item of input hash
+
+ char* sortKeyBuf = NULL; // buffer to store sort keys
+ uint32_t sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; // buffer size
+ ptrdiff_t sortKeyBufOffset = 0; // pos in buffer to store sort key
+ int32_t sortKeyLen = 0; // the length of currently processing key
+ uint32_t bufLeft = 0;
+ uint32_t bufIncrement = 0;
+
+ collator_sort_key_index_t* sortKeyIndxBuf = NULL; // buffer to store 'indexes' which will be passed to 'qsort'
+ uint32_t sortKeyIndxBufSize = DEF_SORT_KEYS_INDX_BUF_SIZE;
+ uint32_t sortKeyIndxSize = sizeof( collator_sort_key_index_t );
+
+ uint32_t sortKeyCount = 0;
+ uint32_t j = 0;
+
+ UChar* utf16_buf = NULL; // tmp buffer to hold current processing string in utf-16
+ int utf16_buf_size = DEF_UTF16_BUF_SIZE; // the length of utf16_buf
+ int utf16_len = 0; // length of converted string
+
+ HashTable* sortedHash = NULL;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+ &object, Collator_ce_ptr, &array ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_sort_with_sort_keys: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+
+ /*
+ * Sort specified array.
+ */
+ hash = HASH_OF( array );
+
+ if( !hash || zend_hash_num_elements( hash ) == 0 )
+ RETURN_TRUE;
+
+ // Create bufers
+ sortKeyBuf = ecalloc( sortKeyBufSize, sizeof( char ) );
+ sortKeyIndxBuf = ecalloc( sortKeyIndxBufSize, sizeof( uint8_t ) );
+ utf16_buf = eumalloc( utf16_buf_size );
+
+ // Iterate through input hash and create a sort key for each value.
+ zend_hash_internal_pointer_reset( hash );
+ while( zend_hash_get_current_data( hash, (void**) &hashData ) == SUCCESS )
+ {
+ // Convert current hash item from UTF-8 to UTF-16LE and save the result to utf16_buf.
+
+ utf16_len = utf16_buf_size;
+
+ // Process string values only.
+ if( Z_TYPE_PP( hashData ) == IS_STRING )
+ {
+ intl_convert_utf8_to_utf16( &utf16_buf, &utf16_len, Z_STRVAL_PP( hashData ), Z_STRLEN_PP( hashData ), COLLATOR_ERROR_CODE_P( co ) );
+
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Sort with sort keys failed", 0 TSRMLS_CC );
+
+ if( utf16_buf )
+ efree( utf16_buf );
+
+ efree( sortKeyIndxBuf );
+ efree( sortKeyBuf );
+
+ RETURN_FALSE;
+ }
+ }
+ else
+ {
+ // Set empty string
+ utf16_len = 0;
+ utf16_buf[utf16_len] = 0;
+ }
+
+ if( (utf16_len + 1) > utf16_buf_size )
+ utf16_buf_size = utf16_len + 1;
+
+ // Get sort key, reallocating the buffer if needed.
+ bufLeft = sortKeyBufSize - sortKeyBufOffset;
+
+ sortKeyLen = ucol_getSortKey( co->ucoll,
+ utf16_buf,
+ utf16_len,
+ (uint8_t*)sortKeyBuf + sortKeyBufOffset,
+ bufLeft );
+
+ // check for sortKeyBuf overflow, increasing its size of the buffer if needed
+ if( sortKeyLen > bufLeft )
+ {
+ bufIncrement = ( sortKeyLen > DEF_SORT_KEYS_BUF_INCREMENT ) ? sortKeyLen : DEF_SORT_KEYS_BUF_INCREMENT;
+
+ sortKeyBufSize += bufIncrement;
+ bufLeft += bufIncrement;
+
+ sortKeyBuf = erealloc( sortKeyBuf, sortKeyBufSize );
+
+ sortKeyLen = ucol_getSortKey( co->ucoll, utf16_buf, utf16_len, (uint8_t*)sortKeyBuf + sortKeyBufOffset, bufLeft );
+ }
+
+ // check sortKeyIndxBuf overflow, increasing its size of the buffer if needed
+ if( ( sortKeyCount + 1 ) * sortKeyIndxSize > sortKeyIndxBufSize )
+ {
+ bufIncrement = ( sortKeyIndxSize > DEF_SORT_KEYS_INDX_BUF_INCREMENT ) ? sortKeyIndxSize : DEF_SORT_KEYS_INDX_BUF_INCREMENT;
+
+ sortKeyIndxBufSize += bufIncrement;
+
+ sortKeyIndxBuf = erealloc( sortKeyIndxBuf, sortKeyIndxBufSize );
+ }
+
+ sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset; // remeber just offset, cause address
+ // of 'sortKeyBuf' may be changed due to realloc.
+ sortKeyIndxBuf[sortKeyCount].zstr = hashData;
+
+ sortKeyBufOffset += sortKeyLen;
+ ++sortKeyCount;
+
+ zend_hash_move_forward( hash );
+ }
+
+ // update ptrs to point to valid keys.
+ for( j = 0; j < sortKeyCount; j++ )
+ sortKeyIndxBuf[j].key = sortKeyBuf + (ptrdiff_t)sortKeyIndxBuf[j].key;
+
+ // sort it
+ zend_qsort( sortKeyIndxBuf, sortKeyCount, sortKeyIndxSize, collator_cmp_sort_keys TSRMLS_CC );
+
+ // for resulting hash we'll assign new hash keys rather then reordering
+ ALLOC_HASHTABLE( sortedHash );
+ zend_hash_init( sortedHash, 0, NULL, ZVAL_PTR_DTOR, 0 );
+
+ for( j = 0; j < sortKeyCount; j++ )
+ {
+ zval_add_ref( sortKeyIndxBuf[j].zstr );
+ zend_hash_next_index_insert( sortedHash, sortKeyIndxBuf[j].zstr, sizeof(zval **), NULL );
+ }
+
+ // Save sorted hash into return variable.
+ zval_dtor( array );
+ (array)->value.ht = sortedHash;
+ (array)->type = IS_ARRAY;
+
+ if( utf16_buf )
+ efree( utf16_buf );
+
+ efree( sortKeyIndxBuf );
+ efree( sortKeyBuf );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool Collator::asort( Collator $coll, array(string) $arr )
+ * Sort array using specified collator, maintaining index association. }}} */
+/* {{{ proto bool collator_asort( Collator $coll, array(string) $arr )
+ * Sort array using specified collator, maintaining index association.
+ */
+PHP_FUNCTION( collator_asort )
+{
+ collator_sort_internal( FALSE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/*
+ * 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/collator/collator_sort.h b/ext/intl/collator/collator_sort.h
new file mode 100755
index 0000000000..0fafb9f35a
--- /dev/null
+++ b/ext/intl/collator/collator_sort.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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_SORT_H
+#define COLLATOR_SORT_H
+
+#include <php.h>
+
+typedef int (*collator_compare_func_t)( zval *result, zval *op1, zval *op2 TSRMLS_DC );
+
+PHP_FUNCTION( collator_sort );
+PHP_FUNCTION( collator_sort_with_sort_keys );
+PHP_FUNCTION( collator_asort );
+
+#endif // COLLATOR_SORT_H
diff --git a/ext/intl/common/common_error.c b/ext/intl/common/common_error.c
new file mode 100755
index 0000000000..3468cd520e
--- /dev/null
+++ b/ext/intl/common/common_error.c
@@ -0,0 +1,266 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "intl_error.h"
+#include "common_error.h"
+
+/* {{{ proto int intl_get_error_code()
+ * Get code of the last occured error.
+ */
+PHP_FUNCTION( intl_get_error_code )
+{
+ RETURN_LONG( intl_error_get_code( NULL TSRMLS_CC ) );
+}
+/* }}} */
+
+/* {{{ proto string intl_get_error_message()
+ * Get text description of the last occured error.
+ */
+PHP_FUNCTION( intl_get_error_message )
+{
+ char* message = intl_error_get_message( NULL TSRMLS_CC );
+ RETURN_STRING( message, FALSE );
+}
+/* }}} */
+
+/* {{{ proto bool intl_is_failure()
+ * Check whether the given error code indicates a failure.
+ * Returns true if it does, and false if the code
+ * indicates success or a warning.
+ */
+PHP_FUNCTION( intl_is_failure )
+{
+ long err_code;
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &err_code ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intl_is_failure: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ RETURN_BOOL( U_FAILURE( err_code ) );
+}
+
+/* {{{ proto string intl_error_name()
+ * Return a string for a given error code.
+ * The string will be the same as the name of the error code constant.
+ */
+PHP_FUNCTION( intl_error_name )
+{
+ long err_code;
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "l",
+ &err_code ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "intl_error_name: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING( (char*)u_errorName( err_code ), 1 );
+}
+/* }}} */
+
+/* {{{ intl_expose_icu_error_codes
+ * Expose ICU error codes
+ */
+void intl_expose_icu_error_codes( INIT_FUNC_ARGS )
+{
+ #define INTL_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+
+ // Warnings
+ INTL_EXPOSE_CONST( U_USING_FALLBACK_WARNING );
+ INTL_EXPOSE_CONST( U_ERROR_WARNING_START );
+ INTL_EXPOSE_CONST( U_USING_DEFAULT_WARNING );
+ INTL_EXPOSE_CONST( U_SAFECLONE_ALLOCATED_WARNING );
+ INTL_EXPOSE_CONST( U_STATE_OLD_WARNING );
+ INTL_EXPOSE_CONST( U_STRING_NOT_TERMINATED_WARNING );
+ INTL_EXPOSE_CONST( U_SORT_KEY_TOO_SHORT_WARNING );
+ INTL_EXPOSE_CONST( U_AMBIGUOUS_ALIAS_WARNING );
+ INTL_EXPOSE_CONST( U_DIFFERENT_UCA_VERSION );
+ INTL_EXPOSE_CONST( U_ERROR_WARNING_LIMIT );
+
+ // Standard errors
+ INTL_EXPOSE_CONST( U_ZERO_ERROR );
+ INTL_EXPOSE_CONST( U_ILLEGAL_ARGUMENT_ERROR );
+ INTL_EXPOSE_CONST( U_MISSING_RESOURCE_ERROR );
+ INTL_EXPOSE_CONST( U_INVALID_FORMAT_ERROR );
+ INTL_EXPOSE_CONST( U_FILE_ACCESS_ERROR );
+ INTL_EXPOSE_CONST( U_INTERNAL_PROGRAM_ERROR );
+ INTL_EXPOSE_CONST( U_MESSAGE_PARSE_ERROR );
+ INTL_EXPOSE_CONST( U_MEMORY_ALLOCATION_ERROR );
+ INTL_EXPOSE_CONST( U_INDEX_OUTOFBOUNDS_ERROR );
+ INTL_EXPOSE_CONST( U_PARSE_ERROR );
+ INTL_EXPOSE_CONST( U_INVALID_CHAR_FOUND );
+ INTL_EXPOSE_CONST( U_TRUNCATED_CHAR_FOUND );
+ INTL_EXPOSE_CONST( U_ILLEGAL_CHAR_FOUND );
+ INTL_EXPOSE_CONST( U_INVALID_TABLE_FORMAT );
+ INTL_EXPOSE_CONST( U_INVALID_TABLE_FILE );
+ INTL_EXPOSE_CONST( U_BUFFER_OVERFLOW_ERROR );
+ INTL_EXPOSE_CONST( U_UNSUPPORTED_ERROR );
+ INTL_EXPOSE_CONST( U_RESOURCE_TYPE_MISMATCH );
+ INTL_EXPOSE_CONST( U_ILLEGAL_ESCAPE_SEQUENCE );
+ INTL_EXPOSE_CONST( U_UNSUPPORTED_ESCAPE_SEQUENCE );
+ INTL_EXPOSE_CONST( U_NO_SPACE_AVAILABLE );
+ INTL_EXPOSE_CONST( U_CE_NOT_FOUND_ERROR );
+ INTL_EXPOSE_CONST( U_PRIMARY_TOO_LONG_ERROR );
+ INTL_EXPOSE_CONST( U_STATE_TOO_OLD_ERROR );
+ INTL_EXPOSE_CONST( U_TOO_MANY_ALIASES_ERROR );
+ INTL_EXPOSE_CONST( U_ENUM_OUT_OF_SYNC_ERROR );
+ INTL_EXPOSE_CONST( U_INVARIANT_CONVERSION_ERROR );
+ INTL_EXPOSE_CONST( U_INVALID_STATE_ERROR );
+ INTL_EXPOSE_CONST( U_COLLATOR_VERSION_MISMATCH );
+ INTL_EXPOSE_CONST( U_USELESS_COLLATOR_ERROR );
+ INTL_EXPOSE_CONST( U_NO_WRITE_PERMISSION );
+ INTL_EXPOSE_CONST( U_STANDARD_ERROR_LIMIT );
+
+ // The error code range 0x10000 0x10100 are reserved for Transliterator
+ INTL_EXPOSE_CONST( U_BAD_VARIABLE_DEFINITION );
+ INTL_EXPOSE_CONST( U_PARSE_ERROR_START );
+ INTL_EXPOSE_CONST( U_MALFORMED_RULE );
+ INTL_EXPOSE_CONST( U_MALFORMED_SET );
+ INTL_EXPOSE_CONST( U_MALFORMED_SYMBOL_REFERENCE );
+ INTL_EXPOSE_CONST( U_MALFORMED_UNICODE_ESCAPE );
+ INTL_EXPOSE_CONST( U_MALFORMED_VARIABLE_DEFINITION );
+ INTL_EXPOSE_CONST( U_MALFORMED_VARIABLE_REFERENCE );
+ INTL_EXPOSE_CONST( U_MISMATCHED_SEGMENT_DELIMITERS );
+ INTL_EXPOSE_CONST( U_MISPLACED_ANCHOR_START );
+ INTL_EXPOSE_CONST( U_MISPLACED_CURSOR_OFFSET );
+ INTL_EXPOSE_CONST( U_MISPLACED_QUANTIFIER );
+ INTL_EXPOSE_CONST( U_MISSING_OPERATOR );
+ INTL_EXPOSE_CONST( U_MISSING_SEGMENT_CLOSE );
+ INTL_EXPOSE_CONST( U_MULTIPLE_ANTE_CONTEXTS );
+ INTL_EXPOSE_CONST( U_MULTIPLE_CURSORS );
+ INTL_EXPOSE_CONST( U_MULTIPLE_POST_CONTEXTS );
+ INTL_EXPOSE_CONST( U_TRAILING_BACKSLASH );
+ INTL_EXPOSE_CONST( U_UNDEFINED_SEGMENT_REFERENCE );
+ INTL_EXPOSE_CONST( U_UNDEFINED_VARIABLE );
+ INTL_EXPOSE_CONST( U_UNQUOTED_SPECIAL );
+ INTL_EXPOSE_CONST( U_UNTERMINATED_QUOTE );
+ INTL_EXPOSE_CONST( U_RULE_MASK_ERROR );
+ INTL_EXPOSE_CONST( U_MISPLACED_COMPOUND_FILTER );
+ INTL_EXPOSE_CONST( U_MULTIPLE_COMPOUND_FILTERS );
+ INTL_EXPOSE_CONST( U_INVALID_RBT_SYNTAX );
+ INTL_EXPOSE_CONST( U_INVALID_PROPERTY_PATTERN );
+ INTL_EXPOSE_CONST( U_MALFORMED_PRAGMA );
+ INTL_EXPOSE_CONST( U_UNCLOSED_SEGMENT );
+ INTL_EXPOSE_CONST( U_ILLEGAL_CHAR_IN_SEGMENT );
+ INTL_EXPOSE_CONST( U_VARIABLE_RANGE_EXHAUSTED );
+ INTL_EXPOSE_CONST( U_VARIABLE_RANGE_OVERLAP );
+ INTL_EXPOSE_CONST( U_ILLEGAL_CHARACTER );
+ INTL_EXPOSE_CONST( U_INTERNAL_TRANSLITERATOR_ERROR );
+ INTL_EXPOSE_CONST( U_INVALID_ID );
+ INTL_EXPOSE_CONST( U_INVALID_FUNCTION );
+ INTL_EXPOSE_CONST( U_PARSE_ERROR_LIMIT );
+
+ // The error code range 0x10100 0x10200 are reserved for formatting API parsing error
+ INTL_EXPOSE_CONST( U_UNEXPECTED_TOKEN );
+ INTL_EXPOSE_CONST( U_FMT_PARSE_ERROR_START );
+ INTL_EXPOSE_CONST( U_MULTIPLE_DECIMAL_SEPARATORS );
+ INTL_EXPOSE_CONST( U_MULTIPLE_DECIMAL_SEPERATORS ); // Typo: kept for backward compatibility. Use U_MULTIPLE_DECIMAL_SEPARATORS
+ INTL_EXPOSE_CONST( U_MULTIPLE_EXPONENTIAL_SYMBOLS );
+ INTL_EXPOSE_CONST( U_MALFORMED_EXPONENTIAL_PATTERN );
+ INTL_EXPOSE_CONST( U_MULTIPLE_PERCENT_SYMBOLS );
+ INTL_EXPOSE_CONST( U_MULTIPLE_PERMILL_SYMBOLS );
+ INTL_EXPOSE_CONST( U_MULTIPLE_PAD_SPECIFIERS );
+ INTL_EXPOSE_CONST( U_PATTERN_SYNTAX_ERROR );
+ INTL_EXPOSE_CONST( U_ILLEGAL_PAD_POSITION );
+ INTL_EXPOSE_CONST( U_UNMATCHED_BRACES );
+ INTL_EXPOSE_CONST( U_UNSUPPORTED_PROPERTY );
+ INTL_EXPOSE_CONST( U_UNSUPPORTED_ATTRIBUTE );
+ INTL_EXPOSE_CONST( U_FMT_PARSE_ERROR_LIMIT );
+
+ // The error code range 0x10200 0x102ff are reserved for Break Iterator related error
+ INTL_EXPOSE_CONST( U_BRK_INTERNAL_ERROR );
+ INTL_EXPOSE_CONST( U_BRK_ERROR_START );
+ INTL_EXPOSE_CONST( U_BRK_HEX_DIGITS_EXPECTED );
+ INTL_EXPOSE_CONST( U_BRK_SEMICOLON_EXPECTED );
+ INTL_EXPOSE_CONST( U_BRK_RULE_SYNTAX );
+ INTL_EXPOSE_CONST( U_BRK_UNCLOSED_SET );
+ INTL_EXPOSE_CONST( U_BRK_ASSIGN_ERROR );
+ INTL_EXPOSE_CONST( U_BRK_VARIABLE_REDFINITION );
+ INTL_EXPOSE_CONST( U_BRK_MISMATCHED_PAREN );
+ INTL_EXPOSE_CONST( U_BRK_NEW_LINE_IN_QUOTED_STRING );
+ INTL_EXPOSE_CONST( U_BRK_UNDEFINED_VARIABLE );
+ INTL_EXPOSE_CONST( U_BRK_INIT_ERROR );
+ INTL_EXPOSE_CONST( U_BRK_RULE_EMPTY_SET );
+ INTL_EXPOSE_CONST( U_BRK_UNRECOGNIZED_OPTION );
+ INTL_EXPOSE_CONST( U_BRK_MALFORMED_RULE_TAG );
+ INTL_EXPOSE_CONST( U_BRK_ERROR_LIMIT );
+
+ // The error codes in the range 0x10300-0x103ff are reserved for regular expression related errrs
+ INTL_EXPOSE_CONST( U_REGEX_INTERNAL_ERROR );
+ INTL_EXPOSE_CONST( U_REGEX_ERROR_START );
+ INTL_EXPOSE_CONST( U_REGEX_RULE_SYNTAX );
+ INTL_EXPOSE_CONST( U_REGEX_INVALID_STATE );
+ INTL_EXPOSE_CONST( U_REGEX_BAD_ESCAPE_SEQUENCE );
+ INTL_EXPOSE_CONST( U_REGEX_PROPERTY_SYNTAX );
+ INTL_EXPOSE_CONST( U_REGEX_UNIMPLEMENTED );
+ INTL_EXPOSE_CONST( U_REGEX_MISMATCHED_PAREN );
+ INTL_EXPOSE_CONST( U_REGEX_NUMBER_TOO_BIG );
+ INTL_EXPOSE_CONST( U_REGEX_BAD_INTERVAL );
+ INTL_EXPOSE_CONST( U_REGEX_MAX_LT_MIN );
+ INTL_EXPOSE_CONST( U_REGEX_INVALID_BACK_REF );
+ INTL_EXPOSE_CONST( U_REGEX_INVALID_FLAG );
+ INTL_EXPOSE_CONST( U_REGEX_LOOK_BEHIND_LIMIT );
+ INTL_EXPOSE_CONST( U_REGEX_SET_CONTAINS_STRING );
+ 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 );
+ INTL_EXPOSE_CONST( U_IDNA_CHECK_BIDI_ERROR );
+ INTL_EXPOSE_CONST( U_IDNA_STD3_ASCII_RULES_ERROR );
+ INTL_EXPOSE_CONST( U_IDNA_ACE_PREFIX_ERROR );
+ 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 );
+#endif
+
+ // Aliases for StringPrep
+ INTL_EXPOSE_CONST( U_STRINGPREP_PROHIBITED_ERROR );
+ INTL_EXPOSE_CONST( U_STRINGPREP_UNASSIGNED_ERROR );
+ INTL_EXPOSE_CONST( U_STRINGPREP_CHECK_BIDI_ERROR );
+
+ INTL_EXPOSE_CONST( U_ERROR_LIMIT );
+
+ #undef INTL_EXPOSE_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/common/common_error.h b/ext/intl/common/common_error.h
new file mode 100755
index 0000000000..8716222124
--- /dev/null
+++ b/ext/intl/common/common_error.h
@@ -0,0 +1,30 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_COMMON_ERROR_H
+#define INTL_COMMON_ERROR_H
+
+#include <php.h>
+
+PHP_FUNCTION( intl_get_error_code );
+PHP_FUNCTION( intl_get_error_message );
+PHP_FUNCTION( intl_is_failure );
+PHP_FUNCTION( intl_error_name );
+
+void intl_expose_icu_error_codes( INIT_FUNC_ARGS );
+
+#endif // INTL_COMMON_ERROR_H
diff --git a/ext/intl/config.m4 b/ext/intl/config.m4
new file mode 100755
index 0000000000..9a6296f672
--- /dev/null
+++ b/ext/intl/config.m4
@@ -0,0 +1,66 @@
+dnl config.m4 for extension intl
+
+dnl ##########################################################################
+dnl Initialize the extension
+PHP_ARG_ENABLE(intl, whether to enable internationalization support,
+[ --enable-intl Enable internationalization support])
+
+if test "$PHP_INTL" != "no"; then
+ PHP_SETUP_ICU(INTL_SHARED_LIBADD)
+ PHP_SUBST(INTL_SHARED_LIBADD)
+ PHP_REQUIRE_CXX()
+
+ PHP_NEW_EXTENSION(intl,
+ php_intl.c \
+ intl_error.c \
+ intl_convert.c \
+ collator/collator.c \
+ collator/collator_class.c \
+ collator/collator_sort.c \
+ collator/collator_convert.c \
+ collator/collator_locale.c \
+ collator/collator_compare.c \
+ collator/collator_attr.c \
+ collator/collator_create.c \
+ collator/collator_is_numeric.c \
+ collator/collator_error.c \
+ common/common_error.c \
+ formatter/formatter.c \
+ formatter/formatter_main.c \
+ formatter/formatter_class.c \
+ formatter/formatter_attr.c \
+ formatter/formatter_data.c \
+ formatter/formatter_format.c \
+ formatter/formatter_parse.c \
+ normalizer/normalizer.c \
+ normalizer/normalizer_class.c \
+ normalizer/normalizer_normalize.c \
+ locale/locale.c \
+ locale/locale_class.c \
+ locale/locale_methods.c \
+ dateformat/dateformat.c \
+ dateformat/dateformat_class.c \
+ dateformat/dateformat_attr.c \
+ dateformat/dateformat_data.c \
+ dateformat/dateformat_format.c \
+ dateformat/dateformat_parse.c \
+ msgformat/msgformat.c \
+ msgformat/msgformat_attr.c \
+ msgformat/msgformat_class.c \
+ msgformat/msgformat_data.c \
+ msgformat/msgformat_format.c \
+ msgformat/msgformat_helpers.cpp \
+ msgformat/msgformat_parse.c \
+ grapheme/grapheme_string.c \
+ grapheme/grapheme_util.c \
+ ,$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)
+ PHP_ADD_BUILD_DIR($ext_builddir/normalizer)
+ PHP_ADD_BUILD_DIR($ext_builddir/dateformat)
+ PHP_ADD_BUILD_DIR($ext_builddir/locale)
+ PHP_ADD_BUILD_DIR($ext_builddir/msgformat)
+ PHP_ADD_BUILD_DIR($ext_builddir/grapheme)
+fi
diff --git a/ext/intl/config.w32 b/ext/intl/config.w32
new file mode 100755
index 0000000000..e793cd8cf8
--- /dev/null
+++ b/ext/intl/config.w32
@@ -0,0 +1,71 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_ENABLE("intl", "Enable internationalization support", "no");
+
+if (PHP_INTL != "no") {
+ if (CHECK_LIB("icuuc.lib", "intl", PHP_INTL) &&
+ CHECK_HEADER_ADD_INCLUDE("unicode/utf.h", "CFLAGS_INTL")) {
+ // always build as shared - zend_strtod.c/ICU type conflict
+ EXTENSION("intl", "php_intl.c intl_convert.c intl_error.c ", true,
+ "/I \"" + configure_module_dirname + "\"");
+ ADD_SOURCES(configure_module_dirname + "/collator", "\
+ collator.c \
+ collator_attr.c \
+ collator_class.c \
+ collator_compare.c \
+ collator_convert.c \
+ collator_create.c \
+ collator_error.c \
+ collator_is_numeric.c \
+ collator_locale.c \
+ collator_sort.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/common", "\
+ common_error.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/formatter", "\
+ formatter.c \
+ formatter_attr.c \
+ formatter_class.c \
+ formatter_data.c \
+ formatter_format.c \
+ formatter_main.c \
+ formatter_parse.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/locale", "\
+ locale.c \
+ locale_class.c \
+ locale_methods.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/msgformat", "\
+ msgformat.c \
+ msgformat_attr.c \
+ msgformat_class.c \
+ msgformat_data.c \
+ msgformat_format.c \
+ msgformat_helpers.cpp \
+ msgformat_parse.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/grapheme", "\
+ grapheme_string.c grapheme_util.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/normalizer", "\
+ normalizer.c \
+ normalizer_class.c \
+ normalizer_normalize.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/dateformat", "\
+ dateformat.c \
+ dateformat_class.c \
+ dateformat_attr.c \
+ dateformat_format.c \
+ dateformat_parse.c \
+ dateformat_data.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 {
+ WARNING("intl not enabled; libraries and/or headers not found");
+ }
+}
diff --git a/ext/intl/doc/Tutorial.txt b/ext/intl/doc/Tutorial.txt
new file mode 100755
index 0000000000..4a66dc1844
--- /dev/null
+++ b/ext/intl/doc/Tutorial.txt
@@ -0,0 +1,239 @@
+1. Collator::getAvailableLocales().
+Return the locales available at the time of the call, including registered locales.
+If a sever error occurs (such as out of memory condition) this will return null.
+If there is no locale data, an empty enumeration will be returned.
+Returned locales list is a strings in format of RFC4646 standart (see http://www.rfc-editor.org/rfc/rfc4646.txt).
+Examle of locales format: 'en_US', 'ru_UA', 'ua_UA' (see http://demo.icu-project.org/icu-bin/locexp).
+
+
+2. Collator::getDisplayName( $obj_locale, $disp_locale ).
+Get name of the object for the desired Locale, in the desired langauge. Both arguments
+must be from getAvailableLocales method.
+
+ @param string $obj_locale Locale to get display name for.
+ @param string $disp_locale Specifies the desired locale for output
+
+Both parameters are case insensitive.
+For locale format see RFC4647 standart in ftp://ftp.rfc-editor.org/in-notes/rfc4647.txt
+
+3. Collator::getLocaleByType( $type ).
+Allow user to select whether she wants information on requested, valid or actual locale.
+Returned locale tag is a string formatted to a RFC4646 standart and normalize to normal form -
+value is a string from
+For example, a collator for "en_US_CALIFORNIA" was requested. In the current state of ICU (2.0),
+the requested locale is "en_US_CALIFORNIA", the valid locale is "en_US" (most specific locale
+supported by ICU) and the actual locale is "root" (the collation data comes unmodified from the UCA)
+The locale is considered supported by ICU if there is a core ICU bundle for that locale (although
+it may be empty).
+
+
+4. VariableTop
+The Variable_Top attribute is only meaningful if the Alternate attribute is not set to NonIgnorable.
+In such a case, it controls which characters count as ignorable. The string value specifies
+the "highest" character (in UCA order) weight that is to be considered ignorable.
+Thus, for example, if a user wanted whitespace to be ignorable, but not any visible characters,
+then s/he would use the value Variable_Top="\u0020" (space). The string should only be a
+single character. All characters of the same primary weight are equivalent, so
+Variable_Top="\u3000" (ideographic space) has the same effect as Variable_Top="\u0020".
+This setting (alone) has little impact on string comparison performance; setting it lower or higher
+will make sort keys slightly shorter or longer respectively.
+
+
+5. Strength
+The ICU Collation Service supports many levels of comparison (named "Levels", but also
+known as "Strengths"). Having these categories enables ICU to sort strings precisely
+according to local conventions. However, by allowing the levels to be selectively
+employed, searching for a string in text can be performed with various matching
+conditions.
+Performance optimizations have been made for ICU collation with the default level
+settings. Performance specific impacts are discussed in the Performance section below.
+Following is a list of the names for each level and an example usage:
+
+1. Primary Level: Typically, this is used to denote differences between base characters
+(for example, "a" < "b"). It is the strongest difference. For example, dictionaries are
+divided into different sections by base character. This is also called the level1
+strength.
+
+2. Secondary Level: Accents in the characters are considered secondary differences (for
+example, "as" < "as" < "at"). Other differences between letters can also be considered
+secondary differences, depending on the language. A secondary difference is ignored
+when there is a primary difference anywhere in the strings. This is also called the
+level2 strength.
+Note: In some languages (such as Danish), certain accented letters are considered to
+be separate base characters. In most languages, however, an accented letter only has a
+secondary difference from the unaccented version of that letter.
+
+3. Tertiary Level: Upper and lower case differences in characters are distinguished at the
+tertiary level (for example, "ao" < "Ao" < "ao"). In addition, a variant of a letter differs
+from the base form on the tertiary level (such as "A" and " "). Another ? example is the
+difference between large and small Kana. A tertiary difference is ignored when there is
+a primary or secondary difference anywhere in the strings. This is also called the level3
+strength.
+
+4. Quaternary Level: When punctuation is ignored (see Ignoring Punctuations ) at level
+13, an additional level can be used to distinguish words with and without punctuation
+(for example, "ab" < "a-b" < "aB"). This difference is ignored when there is a primary,
+secondary or tertiary difference. This is also known as the level4 strength. The
+quaternary level should only be used if ignoring punctuation is required or when
+processing Japanese text (see Hiragana processing).
+
+5. Identical Level: When all other levels are equal, the identical level is used as a
+tiebreaker. The Unicode code point values of the NFD form of each string are
+compared at this level, just in case there is no difference at levels 14
+. For example, Hebrew cantillation marks are only distinguished at this level. This level should be
+used sparingly, as only code point values differences between two strings is an
+extremely rare occurrence. Using this level substantially decreases the performance for
+both incremental comparison and sort key generation (as well as increasing the sort
+key length). It is also known as level 5 strength.
+
+For example, people may choose to ignore accents or ignore accents and case when searching
+for text. Almost all characters are distinguished by the first three levels, and in most
+locales the default value is thus Tertiary. However, if Alternate is set to be Shifted,
+then the Quaternary strength can be used to break ties among whitespace, punctuation, and
+symbols that would otherwise be ignored. If very fine distinctions among characters are required,
+then the Identical strength can be used (for example, Identical Strength distinguishes
+between the Mathematical Bold Small A and the Mathematical Italic Small A.). However, using
+levels higher than Tertiary the Identical strength result in significantly longer sort
+keys, and slower string comparison performance for equal strings.
+
+
+
+6. Collator::__construct( $locale ).
+The Locale attribute is typically the most important attribute for correct sorting and matching,
+according to the user expectations in different countries and regions. The default UCA
+ordering will only sort a few languages such as Dutch and Portuguese correctly ("correctly"
+meaning according to the normal expectations for users of the languages).
+Otherwise, you need to supply the locale to UCA in order to properly collate text for a
+given language. Thus a locale needs to be supplied so as to choose a collator that is correctly
+tailored for that locale. The choice of a locale will automatically preset the values for
+all of the attributes to something that is reasonable for that locale. Thus most of the time the
+other attributes do not need to be explicitly set. In some cases, the choice of locale will make a
+difference in string comparison performance and/or sort key length.
+In short attribute names, <language>_<script>_<region>_<keyword>.
+Not all the elements are required. Valid values for locale elements are general valid values
+for RFC4646 locale naming, and RFC 4647 lookup algorithm.
+Example:
+Locale="sv" (Swedish) "Kypper" < "Kopfe"
+Locale="de" (German) "Kopfe" < "Kypper"
+
+
+7. Collator::get/setAttribute.
+ICU uses UCA as a default starting point for ordering. Not all languages have sorting sequences
+that correspond with the UCA because UCA cannot simultaneously encompass the specifics of all
+the languages currently in use. Therefore, ICU provides a data-driven, flexible, and run-time
+customizable mechanism called "tailoring". Tailoring overrides the default order of code points
+and the values of the ICU Collation Service attributes.
+Collator have followed attributes:
+ - FRENCH_COLLATION, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+ - CASE_FIRST, possible values are:
+ OFF (default)
+ LOWER_FIRST
+ UPPER_FIRST
+ DEFAULT
+
+ - CASE_LEVEL, possible values are:
+ OFF (default)
+ ON
+ DEFAULT
+
+ - NORMALIZATION_MODE, possible values are:
+ OFF (default)
+ ON
+ DEFAULT
+
+ - STRENGTH, possible values are:
+ PRIMARY
+ SECONDARY
+ TERTIARY (default)
+ QUATERNARY
+ IDENTICAL
+ DEFAULT
+
+ - ALTERNATE_HANDLING, possible values are:
+ NON_IGNORABLE (default)
+ SHIFTED
+ DEFAULT
+
+ - HIRAGANA_QUATERNARY_MODE, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+ - NUMERIC_COLLATION, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+Description of all of this attributes:
+
+FRENCH_COLLATION - Sort strings with different accents from the back of the string. This attribute
+is automatically set to On for the French locales and a few others. Users normally would
+not need to explicitly set this attribute. There is a string comparison performance cost when
+it is set On, but sort key length is unaffected.
+Example:
+F=X cote < cote < cote < cote
+F=O cote < cote < cote < cote
+
+CASE_FIRST - The Case_First attribute is used to control whether uppercase letters come before
+lowercase letters or vice versa, in the absence of other differences in the strings. The possible
+values are Uppercase_First (U) and Lowercase_First (L), plus the standard Default and Off.
+There is almost no difference between the Off and Lowercase_First options in terms of results,
+so typically users will not use Lowercase_First: only Off or Uppercase_First. (People interested
+in the detailed differences between X and L should consult the Collation Customization).
+Specifying either L or U won't affect string comparison performance, but will affect the sort key
+length.
+Example:
+C=X or C=L "china" < "China" < "denmark" <
+"Denmark"
+C=U "China" < "china" < "Denmark" < "denmark"
+
+CASE_LEVEL - The Case_Level attribute is used when ignoring accents but not case. In such a situation,
+set Strength to be Primary, and Case_Level to be On. In most locales, this setting is Off by default.
+There is a small string comparison performance and sort key impact if this attribute is set to be On.
+Example:
+S=1, E=X role = Role = role
+S=1, E=O role = role < Role
+
+NORMALIZATION_MODE - The Normalization setting determines whether text is thoroughly normalized
+or not in comparison. Even if the setting is off (which is the default for many locales), text as
+represented in common usage will compare correctly (for details, see UTN #5). Only if the accent
+marks are in noncanonical order will there be a problem. If the setting is On, then the best
+results are guaranteed for all possible text input. There is a medium string comparison performance
+cost if this attribute is On, depending on the frequency of sequences that require normalization.
+There is no significant effect on sort key length. If the input text is known to be in NFD or NFKD
+normalization forms, there is no need to enable this Normalization option.
+
+STRENGTH - see Collator::setStrength chapter.
+
+ALTERNATE_HANDLING - The Alternate attribute is used to control the handling of the socalled
+variable characters in the UCA: whitespace, punctuation and symbols. If Alternate is set to
+NonIgnorable (N), then differences among these characters are of the same importance as
+differences among letters. If Alternate is set to Shifted (S), then these characters are of only
+minor importance. The Shifted value is often used in combination with Strength set to Quaternary.
+In such a case, whitespace, punctuation, and symbols are considered when comparing strings,
+but only if all other aspects of the strings (base letters, accents, and case) are identical.
+If Alternate is not set to Shifted, then there is no difference between a Strength of 3 and
+a Strength of 4. For more information and examples, see
+Variable_Weighting in the UCA (http://www.unicode.org/reports/tr10/#Variable_Weighting).
+The reason the Alternate values are not simply On and Off is that additional Alternate values
+may be added in the future. The UCA option Blanked is expressed with Strength set to 3,
+and Alternate set to Shifted. The default for most locales is NonIgnorable. If Shifted is selected,
+it may be slower if there are many strings that are the same except for punctuation;
+sort key length will not be affected unless the strength level is also increased.
+Example:
+S=3, A=N di Silva < Di Silva < diSilva < U.S.A. < USA
+S=3, A=S di Silva = diSilva < Di Silva < U.S.A. = USA
+S=4, A=S di Silva < diSilva < Di Silva < U.S.A. < USA
+
+HIRAGANA_QUATERNARY_MODE - Compatibility with JIS x 4061 requires the introduction of an additional
+level to distinguish Hiragana and Katakana characters. If compatibility with that standard is required,
+then this attribute should be set On, and the strength set to Quaternary. This will affect sort key
+length and string comparison string comparison performance.
+
+NUMERIC_COLLATION - When turned on, this attribute generates a collation key for the
+numeric value of substrings of digits. This is a way to get '100' to sort AFTER '2'.
+
diff --git a/ext/intl/doc/collator_api.php b/ext/intl/doc/collator_api.php
new file mode 100755
index 0000000000..38da2798fc
--- /dev/null
+++ b/ext/intl/doc/collator_api.php
@@ -0,0 +1,398 @@
+<?php
+#############################################################################
+# Object-oriented API
+#############################################################################
+
+/**
+ * Collator class.
+ *
+ * This is a wrapper around ICU Collator C API (declared in ucol.h).
+ *
+ * Example:
+ * <code>
+ *
+ * </code>
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/ucol_8h.html
+ * @see http://www.icu-project.org/apiref/icu4c/classCollator.html
+ *
+ */
+class Collator {
+#############################################################################
+# Common constants.
+#############################################################################
+
+/**
+ * Locale-related constants.
+ *
+ * These will be moved out of Collator when Locale class is created.
+ */
+ const ULOC_ACTUAL_LOCALE = 0;
+ const ULOC_VALID_LOCALE = 1;
+ const ULOC_REQUESTED_LOCALE = 2;
+
+ /*
+ * WARNING:
+ * The values described here are NOT the actual values in PHP code.
+ * They are references to the ICU C definitions, so the line
+ * const DEFAULT_STRENGTH = 'UCOL_DEFAULT_STRENGTH';
+ * actually means that Collator::DEFAULT_STRENGTH is the same as
+ * UCOL_DEFAULT_STRENGTH constant in the ICU library.
+ */
+ /**
+ * Valid attribute values.
+ *
+ * @see Collator::setAttribute()
+ * @see collator_set_attribute()
+ */
+ const DEFAULT_VALUE = 'UCOL_DEFAULT';
+ const PRIMARY = 'UCOL_PRIMARY';
+ const SECONDARY = 'UCOL_SECONDARY';
+ const TERTIARY = 'UCOL_TERTIARY';
+ const DEFAULT_STRENGTH = 'UCOL_DEFAULT_STRENGTH';
+ const QUATERNARY = 'UCOL_QUATERNARY';
+ const IDENTICAL = 'UCOL_IDENTICAL';
+ const OFF = 'UCOL_OFF';
+ const ON = 'UCOL_ON';
+ const SHIFTED = 'UCOL_SHIFTED';
+ const NON_IGNORABLE = 'UCOL_NON_IGNORABLE';
+ const LOWER_FIRST = 'UCOL_LOWER_FIRST';
+ const UPPER_FIRST = 'UCOL_UPPER_FIRST';
+
+ /**
+ * Valid attribute names.
+ *
+ * @see Collator::setAttribute()
+ * @see collator_set_attribute()
+ */
+ const FRENCH_COLLATION = 'UCOL_FRENCH_COLLATION';
+ const ALTERNATE_HANDLING = 'UCOL_ALTERNATE_HANDLING';
+ const CASE_FIRST = 'UCOL_CASE_FIRST';
+ const CASE_LEVEL = 'UCOL_CASE_LEVEL';
+ const NORMALIZATION_MODE = 'UCOL_NORMALIZATION_MODE';
+ const STRENGTH = 'UCOL_STRENGTH';
+ const HIRAGANA_QUATERNARY_MODE = 'UCOL_HIRAGANA_QUATERNARY_MODE';
+ const NUMERIC_COLLATION = 'UCOL_NUMERIC_COLLATION';
+
+ /**
+ * Create a collator
+ *
+ * @param string $locale The locale whose collation rules
+ * should be used. Special values for
+ * locales can be passed in - if null is
+ * passed for the locale, the default
+ * locale collation rules will be used. If
+ * empty string ("") or "root" are passed,
+ * UCA rules will be used.
+ *
+ * @return Collator New instance of Collator object.
+ */
+ public function __construct( $locale ) {}
+
+ /**
+ * Create a collator
+ *
+ * Creates a new instance of Collator.
+ *
+ * This method is useful when you prefer just to get null on error,
+ * as if you called collator_create().
+ *
+ * @return Collator Newly created Collator instance,
+ * or null on error.
+ *
+ * @see __construct()
+ * @see collator_create()
+ */
+ public static function create( $locale ) {}
+
+ /**
+ * Get collator's last error code.
+ *
+ * @return int Error code returned by the last
+ * Collator method call.
+ */
+ public function getErrorCode() {}
+
+ /**
+ * Return error text for the last ICU operation.
+ *
+ * @return string Description of an error occured in the last
+ * Collator method call.
+ */
+ public function getErrorMessage() {}
+
+ /**
+ * Compare two strings using PHP strcmp() semantics.
+ *
+ * Wrapper around ICU ucol_strcoll().
+ *
+ * @param string $str1 First string to compare.
+ * @param string $str2 Second string to compare.
+ *
+ * @return int 1 if $str1 is greater than $str2;
+ * 0 if $str1 is equal to $str2;
+ * -1 if $str1 is less than $str2.
+ * On error false is returned.
+ */
+ public function compare( $str1, $str2 ) {}
+
+ /**
+ * Equivalent to standard PHP sort() using Collator.
+ *
+ * @param array $arr Array of strings to sort
+ * @param int $sort_flags Optional sorting type, one of the following:
+ * - SORT_REGULAR - compare items normally (don't change types)
+ * - SORT_NUMERIC - compare items numerically
+ * - SORT_STRING - compare items as strings
+ * Default sorting type is SORT_REGULAR.
+ *
+ * @return bool true on success or false on failure.
+ */
+ public function sort( $arr, $sort_flags ) {}
+
+ /**
+ * Sort array maintaining index association.
+ *
+ * Equivalent to standard PHP asort() using Collator.
+ *
+ * @param array $arr Array of strings to sort
+ * @param int $sort_flags Optional sorting type
+ *
+ * @return bool true on success or false on failure.
+ *
+ * @see Collator::sort()
+ */
+ public function asort( $arr, $sort_flags ) {}
+
+ /**
+ * Equivalent to standard PHP sort() using Collator.
+ *
+ * Similar to Collator::sort().
+ * Uses ICU ucol_getSortKey() to gain more speed on large arrays.
+ *
+ * @param array $arr Array of strings to sort
+ *
+ * @return bool true on success or false on failure.
+ */
+ public function sortWithSortKeys( $arr ) {}
+
+ /**
+ * @todo Do we want to support other standard PHP sort functions: ksort, rsort, asort?
+ */
+
+ /**
+ * Get collation attribute value.
+ *
+ * Wrapper around ICU ucol_getAttribute().
+ *
+ * @param int $attr Attribute to get value for.
+ *
+ * @return int Attribute value, or false on error.
+ */
+ public function getAttribute( $attr ) {}
+
+ /**
+ * Set collation attribute.
+ *
+ * Wrapper around ICU ucol_setAttribute().
+ *
+ * @param int $attr Attribute.
+ * @param int $val Attribute value.
+ *
+ * @return bool true on success, false otherwise.
+ */
+ public function setAttribute( $attr, $val ) {}
+
+ /**
+ * Get current collation strength.
+ *
+ * Wrapper around ICU ucol_getStrength().
+ *
+ * @return int Current collation strength, or false on error.
+ */
+ public function getStrength() {}
+
+ /**
+ * Set collation strength.
+ *
+ * Wrapper around ICU ucol_setStrength().
+ *
+ * @param int $strength Strength to set.
+ *
+ * @return bool true on success, false otherwise.
+ */
+ public function setStrength( $strength ) {}
+
+ /**
+ * Get the locale name of the collator.
+ *
+ * Wrapper around ICU ucol_getLocaleByType().
+ *
+ * @param int $type You can choose between requested, valid
+ * and actual locale
+ * (ULOC_REQUESTED_LOCALE,
+ * ULOC_VALID_LOCALE, ULOC_ACTUAL_LOCALE,
+ * respectively).
+ *
+ * @return string Real locale name from which the
+ * collation data comes. If the collator
+ * was instantiated from rules or an error occured,
+ * returns false.
+ */
+ public function getLocale( $type ) {}
+}
+
+#############################################################################
+# Procedural API
+#############################################################################
+
+/**
+ * Create collator.
+ *
+ * @param string $locale The locale containing the required
+ * collation rules. Special values for
+ * locales can be passed in - if null is
+ * passed for the locale, the default
+ * locale collation rules will be used. If
+ * empty string ("") or "root" are passed,
+ * UCA rules will be used.
+ *
+ * @return Collator New instance of Collator object, or null on error.
+ */
+function collator_create( $locale ) {}
+
+/**
+ * Compare two strings.
+ *
+ * The strings will be compared using the options already
+ * specified.
+ *
+ * @param Collator $coll Collator object.
+ * @param string $str1 The first string to compare.
+ * @param string $str2 The second string to compare.
+ *
+ * @return int 1 if $str1 is greater than $str2;
+ * 0 if $str1 is equal to $str2;
+ * -1 if $str1 is less than $str2.
+ * On error false is returned.
+ *
+ */
+function collator_compare( $coll, $str1, $str2 ) {}
+
+/**
+ * Sort array using specified collator.
+ *
+ * @param Collator $coll Collator object.
+ * @param array $arr Array of strings to sort.
+ * @param int $sort_flags Optional sorting type, one of the following:
+ * - SORT_REGULAR - compare items normally (don't change types)
+ * - SORT_NUMERIC - compare items numerically
+ * - SORT_STRING - compare items as strings
+ * Default sorting type is SORT_REGULAR.
+ *
+ * @return bool true on success or false on failure.
+ */
+function collator_sort( $coll, $arr, $sort_flags ) {}
+
+/**
+ * Sort array maintaining index association.
+ *
+ * @param Collator $coll Collator object.
+ * @param array $arr Array of strings to sort.
+ * @param int $sort_flags Optional sorting type.
+ *
+ * @return bool true on success or false on failure.
+ *
+ * @see collator_sort()
+ */
+function collator_asort( $coll, $arr, $sort_flags ) {}
+
+/**
+ * Sort array using specified collator.
+ *
+ * Similar to collator_sort().
+ * Uses ICU ucol_getSortKey() to gain more speed on large arrays.
+ *
+ * @param Collator $coll Collator object.
+ * @param array $arr Array of strings to sort
+ *
+ * @return bool true on success or false on failure.
+ */
+function collator_sort_with_sort_keys( $coll, $arr ) {}
+
+/**
+ * Get the locale name of the collator.
+ *
+ * @param Collator $coll Collator object.
+ * @param int $type You can choose between valid and
+ * actual locale
+ * (ULOC_VALID_LOCALE, ULOC_ACTUAL_LOCALE
+ * respectively).
+ *
+ * @return string Real locale name from which the
+ * collation data comes. If the collator
+ * was instantiated from rules or an error occured,
+ * returns false.
+ */
+function collator_get_locale( $coll, $type ) {}
+
+/**
+ * Get collation attribute value.
+ *
+ * @param Collator $coll Collator object.
+ * @param int $attr Attribute to get value for.
+ *
+ * @return int Attribute value, or false on error.
+ */
+function collator_get_attribute( $coll, $attr ) {}
+
+/**
+ * Get current collation strength.
+ *
+ * @param Collator $coll Collator object.
+ *
+ * @return int Current collation strength, or false on error.
+ */
+function collator_get_strength( $coll ) {}
+
+/**
+ * Set collation strength.
+ *
+ * @param Collator $coll Collator object.
+ * @param int $strength Strength to set.
+ *
+ * @return bool true on success, false otherwise.
+ */
+function collator_set_strength( $coll, $strength ) {}
+
+/**
+ * Set collation attribute.
+ *
+ * @param Collator $coll Collator object.
+ * @param int $attr Attribute.
+ * @param int $val Attribute value.
+ *
+ * @return bool true on success, false otherwise.
+ */
+function collator_set_attribute( $coll, $attr, $val ) {}
+
+/**
+ * Get collator's last error code.
+ *
+ * @param Collator $coll Collator object.
+ *
+ * @return int Error code returned by the last
+ * Collator API function call.
+ */
+function collator_get_error_code( $coll ) {}
+
+/**
+ * Get text for collator's last error code.
+ *
+ * @param Collator $coll Collator object.
+ *
+ * @return string Description of an error occured 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
new file mode 100755
index 0000000000..76ba9d88c8
--- /dev/null
+++ b/ext/intl/doc/common_api.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Handling of errors occured in static methods
+ * when there's no object to get error code/message from.
+ *
+ * Example #1:
+ * <code>
+ * $coll = collator_create( '<bad_param>' );
+ * if( !$coll )
+ * handle_error( intl_get_error_code() );
+ * </code>
+ *
+ * Example #2:
+ * <code>
+ * if( Collator::getAvailableLocales() === false )
+ * show_error( intl_get_error_message() );
+ * </code>
+ */
+
+/**
+ * Get the last error code.
+ *
+ * @return int Error code returned by the last
+ * API function call.
+ */
+function intl_get_error_code() {}
+
+/**
+ * Get description of the last error.
+ *
+ * @return string Description of an error occured in the last
+ * API function call.
+ */
+function intl_get_error_message() {}
+
+/**
+ * Check whether the given error code indicates failure.
+ *
+ * @param int $code ICU error code.
+ *
+ * @return bool true if it the code indicates some failure,
+ * and false in case of success or a warning.
+ */
+function intl_is_failure($code) {}
+
+/**
+ * Get symbolic name for a given error code.
+ *
+ * The returned string will be the same as the name of the error code constant.
+ *
+ * @param int $code ICU error code.
+ *
+ * @return string Error code name.
+ */
+function intl_error_name($code) {}
+
+?>
diff --git a/ext/intl/doc/datefmt_api.php b/ext/intl/doc/datefmt_api.php
new file mode 100755
index 0000000000..37249292bb
--- /dev/null
+++ b/ext/intl/doc/datefmt_api.php
@@ -0,0 +1,439 @@
+<?php
+
+/**
+ * Date Formatter class - locale-dependent formatting/parsing of dates using pattern strings and/or canned patterns.
+ *
+ * This class represents the ICU date formatting functionality. It allows users to
+ * display dates in a localized format or to parse strings
+ * into PHP date values using pattern strings and/or canned patterns.
+ *
+ * Example:
+ * <code>
+ * $datefmt = new DateFormatter("de-DE", LONG, SHORT, date_default_timezone_get());
+ * echo $formatter->format(time());
+ * </code>
+ *
+ * <code>
+ * $datefmt = new DateFormatter("de-DE", LONG, SHORT, date_default_timezone_get() , GREGORIAN , "yyyy-MM-dd HH:mm:ss z");
+ * echo $formatter->format(time());
+ * </code>
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/udat_8h.html
+ *
+ */
+class DateFormatter {
+
+#############################################################################
+# Common constants.
+#############################################################################
+
+ /**
+ * The following constants are used to specify different formats
+ * in the constructor.
+ */
+ const NONE = -1;
+ const FULL = 0;
+ const LONG = 1;
+ const MEDIUM = 2;
+ const SHORT = 3;
+
+ /**
+ * The following int constants are used to specify the calendar.
+ * These calendars are all based directly on the Gregorian calendar
+ * Non-Gregorian calendars need to be specified in locale.
+ * Examples might include locale="hi@calendar=BUDDHIST"
+ */
+ const TRADITIONAL = 0; // non-Gregorian calendar that is locale-defined, required by ICU
+ const GREGORIAN = 1 ;// Gregorian calendar
+
+#############################################################################
+# Object-oriented API
+#############################################################################
+ /**
+ * Create a date formatter
+ *
+ * @param string $locale Locale to use when formatting or parsing
+ * @param integer $datetype Date type to use (none, short, medium, long, full)
+ * @param integer $timetype Time type to use (none, short, medium, long, full)
+ * @param [String] $timezone Time zone ID ; default is system default
+ * @param [integer] $calendar Calendar to use for formatting or parsing; default is
+ * GREGORIAN
+ * @param [string] $pattern Optional pattern to use when formatting or parsing
+ * @return DateFormatter
+ * @see __construct
+ * @see datefmt_create
+ */
+ public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar= null , $pattern= null) {}
+
+ /**
+ * Create a date formatter
+ *
+ * @param string $locale Locale to use when formatting or parsing
+ * @param integer $datetype Date type to use (none, short, medium, long, full)
+ * @param integer $timetype Time type to use (none, short, medium, long, full)
+ * @param [string] $timezone Time zone ID ; default is system default
+ * @param [integer] $calendar Calendar to use for formatting or parsing; default is
+ * GREGORIAN
+ * @param [string] $pattern Optional pattern to use when formatting or parsing
+ * @return DateFormatter
+ * @see __construct
+ * @see datefmt_create
+ */
+ public static function create($locale, $datetype, $timetype, $timezone = null, $calendar= null , $pattern= null) {}
+
+ /**
+ * formats the time value as a string.
+ * @param mixed $value - value to format
+ * integer: a unix timestamp value (seconds since epoch, UTC)
+ * array: a localtime array - uses 24 hour clock in tm_hour field
+ * @return string a formatted string or, if an error occurred, 'null'.
+ */
+ public function format($value) {}
+
+
+ /**
+ * converts string $value to an incremental time value, starting at
+ * $parse_pos and consuming as much of the input value as possible
+ * If no error occurs before $value is consumed, $parse_pos will contain -1
+ * otherwise it will contain the position at which parsing ended (and the error
+ * occurred).
+ * @param string $value string to convert to a time
+ * @param integer $parse_pos position at which to start the parsing in $value (zero-based)
+ * This variable will contain the end position if the parse fails
+ * If $parse_pos > strlen($value), the parse fails immediately.
+ * @return integer timestamp parsed value
+ */
+ public function parse($value, $parse_pos=0) {}
+
+
+ /**
+ * converts string $value to a field-based time value, starting at
+ * $parse_pos and consuming as much of the input value as possible
+ * If no error occurs before $value is consumed, $parse_pos will contain -1
+ * otherwise it will contain the position at which parsing ended (and the error
+ * occurred).
+ * @param string $value string to convert to a time
+ * @param integer $parse_pos position at which to start the parsing in $value (zero-based)
+ * This variable will contain the end position if the parse fails
+ * If $parse_pos > strlen($value), the parse fails immediately.
+ * @return array localtime compatible array of integers - uses 24 hour clock in tm_hour field
+ */
+ public function localtime($value, $parse_pos=0) {}
+
+
+ /**
+ * Gets the datetype in use
+ * @return integer the current 'datetype' value of the formatter
+ */
+ public function getDateType() {}
+
+
+ /**
+ * Gets the timetype in use
+ * @return integer the current 'timetype' value of the formatter
+ */
+ public function getTimeType() {}
+
+
+ /**
+ * Gets the leniency in use
+ * @return boolean 'true' if parser is lenient, 'false' if parser is strict
+ * default value for parser is 'false'.
+ */
+ public function isLenient() {}
+
+
+ /**
+ * Sets the leniency to use
+ * @param boolean $lenient sets whether the parser is lenient or not, default is 'false'
+ * 'true' sets the parser to accept otherwise flawed date or
+ * time patterns, parsing as much as possible to obtain a value.
+ * 'false' sets the parser to strictly parse strings into dates.
+ * Extra space, unrecognized tokens, or invalid values
+ * ("Feburary 30th") are not accepted.
+ *
+ * @return boolean 'true' if successful; 'false' if an error occurred.
+ */
+ public function setLenient($lenient) {}
+
+
+ /**
+ * Gets the locale in use
+ * @param [integer] which locale should be returned?
+ * values may include ULOC_ACTUAL_LOCALE,
+ * ULOC_VALID_LOCALE. By default the actual
+ * locale is returned.
+ * @return string the locale of this formatter or 'false' if error
+ */
+
+ public function getLocale($type = ULOC_ACTUAL_LOCALE) {}
+
+
+ /**
+ * @return string ID string for the time zone used by this formatter
+ */
+ public function getTimeZoneId() {}
+
+
+ /**
+ * sets the time zone to use
+ * @param string $zone zone ID string of the time zone to use.
+ * if null or the empty string, the default time zone for
+ * the runtime is used.
+ * @return boolean 'true' on successful setting of the time zone, 'false'
+ * if an error occurred (such as the time zone wasn't recognized).
+ */
+ public function setTimeZoneId($zone) {}
+
+
+ /**
+ * Sets the calendar used to the appropriate calendar, which must be
+ * one of the constants defined above. Some examples include:
+ * - Gregorian calendar
+ * - Traditional
+ * Default value is GREGORIAN
+ * @param integer $which the calendar (an enumerated constant) to use.
+ * @return boolean 'true' if successful, 'false' if an error occurred or if the calendar was not recognized
+ */
+ public function setCalendar($which) {}
+
+
+ /**
+ * Gets the Calendar in use
+ * @return integer the calendar being used by the formatter
+ */
+ public function getCalendar() {}
+
+
+ /**
+ * Gets the pattern in use
+ * @return string the pattern string being used to format/parse
+ */
+ public function getPattern() {}
+
+
+ /**
+ * 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
+ * strings are usually the cause of the latter.
+ */
+ public function setPattern($pattern) {}
+
+
+ /**
+ * Get the error code from last operation
+ *
+ * Returns error code from the last number formatting operation.
+ *
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+ public function getErrorCode() {}
+
+
+ /**
+ * Get the error text from the last operation.
+ *
+ * @return string Description of the last error.
+ */
+ public function getErrorMessage() {}
+
+
+}
+
+#############################################################################
+# Procedural API
+#############################################################################
+
+
+ /**
+ * Create a date formatter
+ *
+ * @param string $locale Locale to use when formatting or parsing
+ * @param integer $datetype Date type to use (none, short, medium, long, full)
+ * @param integer $timetype Time type to use (none, short, medium, long, full)
+ * @param [string] $timezone Time zone ID ; default is system default
+ * @param [integer] $calendar Calendar to use for formatting or parsing; default is
+ * GREGORIAN
+ * @param [string] $pattern Optional pattern to use when formatting or parsing
+ * @return DateFormatter
+ * @see datefmt_create
+ */
+ function datefmt_create($locale, $datetype, $timetype, $timezone = null, $calendar= null ,$pattern=null ) {}
+
+
+ /**
+ * formats the time value as a string.
+ * @param DateFormatter $fmt The date formatter resource
+ * @param mixed $value - value to format
+ * integer: a unix timestamp value (seconds since epoch, UTC)
+ * array: a localtime array - uses 24 hour clock in tm_hour field
+ * @return string a formatted string or, if an error occurred, 'null'.
+ */
+ function datefmt_format($fmt , $value) {}
+
+
+ /**
+ * converts string $value to an incremental time value, starting at
+ * $parse_pos and consuming as much of the input value as possible
+ * If no error occurs before $value is consumed, $parse_pos will contain -1
+ * otherwise it will contain the position at which parsing ended (and the error
+ * occurred).
+ * @param DateFormatter $fmt The date formatter resource
+ * @param string $value string to convert to a time
+ * @param integer $parse_pos position at which to start the parsing in $value (zero-based)
+ * This variable will contain the end position if the parse fails
+ * If $parse_pos > strlen($value), the parse fails immediately.
+ * @return integer timestamp parsed value
+ */
+ function datefmt_parse($fmt , $value, $parse_pos=0) {}
+
+
+ /**
+ * converts string $value to a field-based time value, starting at
+ * $parse_pos and consuming as much of the input value as possible
+ * If no error occurs before $value is consumed, $parse_pos will contain -1
+ * otherwise it will contain the position at which parsing ended (and the error
+ * occurred).
+ * @param DateFormatter $fmt The date formatter resource
+ * @param string $value string to convert to a time
+ * @param integer $parse_pos position at which to start the parsing in $value (zero-based)
+ * This variable will contain the end position if the parse fails
+ * If $parse_pos > strlen($value), the parse fails immediately.
+ * @return array localtime compatible array of integers - uses 24 hour clock in tm_hour field
+ */
+ function datefmt_localtime($fmt , $value, $parse_pos=0) {}
+
+
+ /**
+ * Gets the Datetype in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @return integer the current 'datetype' value of the formatter
+ */
+ function datefmt_get_datetype($fmt ) {}
+
+
+ /**
+ * Gets the timetype in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @return integer the current 'timetype' value of the formatter
+ */
+ function datefmt_get_timetype($fmt) {}
+
+
+ /**
+ * Gets the leniency of the formatter
+ * @param DateFormatter $fmt The date formatter resource
+ * @return boolean 'true' if parser is lenient, 'false' if parser is strict
+ * default value for parser is 'false'.
+ */
+ function datefmt_is_lenient($fmt) {}
+
+
+ /**
+ * Sets the leniency of the formatter
+ * @param DateFormatter $fmt The date formatter resource
+ * @param boolean $lenient sets whether the parser is lenient or not, default is 'false'
+ * 'true' sets the parser to accept otherwise flawed date or
+ * time patterns, parsing as much as possible to obtain a value.
+ * 'false' sets the parser to strictly parse strings into dates.
+ * Extra space, unrecognized tokens, or invalid values
+ * ("Feburary 30th") are not accepted.
+ *
+ * @return boolean 'true' if successful; 'false' if an error occurred.
+ */
+ function datefmt_set_lenient($fmt , $lenient) {}
+
+
+ /**
+ * Gets the locale in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @param [integer] which locale should be returned?
+ * values may include ULOC_ACTUAL_LOCALE,
+ * ULOC_VALID_LOCALE. By default the actual
+ * locale is returned.
+ * @return string the locale of this formatter or 'false' if error
+ */
+ function datefmt_get_locale($fmt , $type = ULOC_ACTUAL_LOCALE) {}
+
+
+ /**
+ * Gets the time zone id in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @return string ID string for the time zone used by this formatter
+ */
+ function datefmt_get_timezone_id($fmt) {}
+
+
+ /**
+ * Sets the time zone to use
+ * @param DateFormatter $fmt The date formatter resource
+ * @param string $zone zone ID string of the time zone to use.
+ * if null or the empty string, the default time zone for
+ * the runtime is used.
+ * @return boolean 'true' on successful setting of the time zone, 'false'
+ * if an error occurred (such as the time zone wasn't recognized).
+ */
+ function datefmt_set_timezone_id($fmt , $zone) {}
+
+
+ /**
+ * Sets the calendar used to the appropriate calendar, which must be
+ * one of the constants defined above. Some examples include:
+ * - Gregorian calendar
+ * - Traditional
+ * Default value is GREGORIAN
+ * @param DateFormatter $fmt The date formatter resource
+ * @param integer $which the calendar (an enumerated constant) to use.
+ * @return boolean 'true' if successful, 'false' if an error occurred or if the calendar was not recognized
+ */
+ function datefmt_set_calendar($fmt , $which) {}
+
+
+ /**
+ * Gets the calendar in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @return integer the calendar being used by the formatter
+ */
+ function datefmt_get_calendar($fmt) {}
+
+
+ /**
+ * Gets the pattern in use
+ * @param DateFormatter $fmt The date formatter resource
+ * @return string the pattern string being used to format/parse
+ */
+ function datefmt_get_pattern($fmt) {}
+
+
+ /**
+ * 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
+ * strings are usually the cause of the latter.
+ */
+ function datefmt_set_pattern($fmt , $pattern) {}
+
+
+ /**
+ * Get the error code from last operation
+ *
+ * @param DateFormatter $fmt The date formatter resource
+ * Returns error code from the last number formatting operation.
+ *
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+ function datefmt_get_error_code($fmt) {}
+
+
+ /**
+ * Get the error text from the last operation.
+ *
+ * @param DateFormatter $fmt The date formatter resource
+ * @return string Description of the last error.
+ */
+ function datefmt_get_error_message($fmt) {}
+
+
+?>
diff --git a/ext/intl/doc/formatter_api.php b/ext/intl/doc/formatter_api.php
new file mode 100755
index 0000000000..c75596e4c9
--- /dev/null
+++ b/ext/intl/doc/formatter_api.php
@@ -0,0 +1,502 @@
+<?php
+
+/**
+ * Number formatter class - locale-dependent number formatting/parsing.
+ *
+ * This class represents the ICU number formatting functionality. It allows to display
+ * number according to the localized format or given pattern or set of rules, and to
+ * parse strings into numbers according to the above patterns.
+ *
+ * Example:
+ * <code>
+ * $value = 1234567;
+ * $formatter = new NumberFormatter("de_DE", NumberFormatter::DECIMAL);
+ * echo $formatter->format($value);
+ * </code>
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/unum_8h.html
+ * @see http://www.icu-project.org/apiref/icu4c/classNumberFormat.html
+ *
+ * The class would also contain all the constants listed in the following enums:
+ * UNumberFormatStyle, UNumberFormatRoundingMode, UNumberFormatPadPosition,
+ * UNumberFormatAttribute, UNumberFormatTextAttribute, UNumberFormatSymbol.
+ */
+class NumberFormatter {
+#############################################################################
+# Common constants.
+#############################################################################
+
+ /*
+ * WARNING:
+ * The values described here are NOT the actual values in PHP code.
+ * They are references to the ICU C definitions, so the line
+ * const PATTERN_DECIMAL = 'UNUM_PATTERN_DECIMAL';
+ * actually means that NumberFormatter::PATTERN_DECIMAL is the same as
+ * UNUM_PATTERN_DECIMAL constant in the ICU library.
+ */
+
+ /*
+ * These constants define formatter/parser argument type - integer, floating point or currency.
+ */
+ const TYPE_DEFAULT = 'FORMAT_TYPE_DEFAULT';
+ const TYPE_INT32 = 'FORMAT_TYPE_INT32';
+ const TYPE_INT64 = 'FORMAT_TYPE_INT64';
+ const TYPE_DOUBLE = 'FORMAT_TYPE_DOUBLE';
+ const TYPE_CURRENCY = 'FORMAT_TYPE_CURRENCY';
+
+ /*
+ * UNumberFormatStyle constants
+ */
+ const PATTERN_DECIMAL = 'UNUM_PATTERN_DECIMAL';
+ const DECIMAL = 'UNUM_DECIMAL';
+ const CURRENCY = 'UNUM_CURRENCY';
+ const PERCENT = 'UNUM_PERCENT';
+ const SCIENTIFIC = 'UNUM_SCIENTIFIC';
+ const SPELLOUT = 'UNUM_SPELLOUT';
+ const ORDINAL = 'UNUM_ORDINAL';
+ const DURATION = 'UNUM_DURATION';
+ const PATTERN_RULEBASED = 'UNUM_PATTERN_RULEBASED';
+ const DEFAULT = 'UNUM_DEFAULT';
+ const IGNORE = 'UNUM_IGNORE';
+
+ /*
+ * UNumberFormatRoundingMode
+ */
+ const ROUND_CEILING = 'UNUM_ROUND_CEILING';
+ const ROUND_FLOOR = 'UNUM_ROUND_FLOOR';
+ const ROUND_DOWN = 'UNUM_ROUND_DOWN';
+ const ROUND_UP = 'UNUM_ROUND_UP';
+ const ROUND_HALFEVEN = 'UNUM_ROUND_HALFEVEN';
+ const ROUND_HALFDOWN = 'UNUM_ROUND_HALFDOWN';
+ const ROUND_HALFUP = 'UNUM_ROUND_HALFUP';
+
+ /*
+ * UNumberFormatPadPosition
+ */
+ const PAD_BEFORE_PREFIX = 'UNUM_PAD_BEFORE_PREFIX';
+ const PAD_AFTER_PREFIX = 'UNUM_PAD_AFTER_PREFIX';
+ const PAD_BEFORE_SUFFIX = 'UNUM_PAD_BEFORE_SUFFIX';
+ const PAD_AFTER_SUFFIX = 'UNUM_PAD_AFTER_SUFFIX';
+
+ /*
+ * UNumberFormatAttribute
+ */
+ const PARSE_INT_ONLY = 'UNUM_PARSE_INT_ONLY';
+ const GROUPING_USED = 'UNUM_GROUPING_USED';
+ const DECIMAL_ALWAYS_SHOWN = 'UNUM_DECIMAL_ALWAYS_SHOWN';
+ const MAX_INTEGER_DIGITS = 'UNUM_MAX_INTEGER_DIGITS';
+ const MIN_INTEGER_DIGITS = 'UNUM_MIN_INTEGER_DIGITS';
+ const INTEGER_DIGITS = 'UNUM_INTEGER_DIGITS';
+ const MAX_FRACTION_DIGITS = 'UNUM_MAX_FRACTION_DIGITS';
+ const MIN_FRACTION_DIGITS = 'UNUM_MIN_FRACTION_DIGITS';
+ const FRACTION_DIGITS = 'UNUM_FRACTION_DIGITS';
+ const MULTIPLIER = 'UNUM_MULTIPLIER';
+ const GROUPING_SIZE = 'UNUM_GROUPING_SIZE';
+ const ROUNDING_MODE = 'UNUM_ROUNDING_MODE';
+ const ROUNDING_INCREMENT = 'UNUM_ROUNDING_INCREMENT';
+ const FORMAT_WIDTH = 'UNUM_FORMAT_WIDTH';
+ const PADDING_POSITION = 'UNUM_PADDING_POSITION';
+ const SECONDARY_GROUPING_SIZE = 'UNUM_SECONDARY_GROUPING_SIZE';
+ const SIGNIFICANT_DIGITS_USED = 'UNUM_SIGNIFICANT_DIGITS_USED';
+ const MIN_SIGNIFICANT_DIGITS = 'UNUM_MIN_SIGNIFICANT_DIGITS';
+ const MAX_SIGNIFICANT_DIGITS = 'UNUM_MAX_SIGNIFICANT_DIGITS';
+ const LENIENT_PARSE = 'UNUM_LENIENT_PARSE';
+
+ /*
+ * UNumberFormatTextAttribute
+ */
+ const POSITIVE_PREFIX = 'UNUM_POSITIVE_PREFIX';
+ const POSITIVE_SUFFIX = 'UNUM_POSITIVE_SUFFIX';
+ const NEGATIVE_PREFIX = 'UNUM_NEGATIVE_PREFIX';
+ const NEGATIVE_SUFFIX = 'UNUM_NEGATIVE_SUFFIX';
+ const PADDING_CHARACTER = 'UNUM_PADDING_CHARACTER';
+ const CURRENCY_CODE = 'UNUM_CURRENCY_CODE';
+ const DEFAULT_RULESET = 'UNUM_DEFAULT_RULESET';
+ const PUBLIC_RULESETS = 'UNUM_PUBLIC_RULESETS';
+
+ /*
+ * UNumberFormatSymbol
+ */
+ const DECIMAL_SEPARATOR_SYMBOL = 'UNUM_DECIMAL_SEPARATOR_SYMBOL';
+ const GROUPING_SEPARATOR_SYMBOL = 'UNUM_GROUPING_SEPARATOR_SYMBOL';
+ const PATTERN_SEPARATOR_SYMBOL = 'UNUM_PATTERN_SEPARATOR_SYMBOL';
+ const PERCENT_SYMBOL = 'UNUM_PERCENT_SYMBOL';
+ const ZERO_DIGIT_SYMBOL = 'UNUM_ZERO_DIGIT_SYMBOL';
+ const DIGIT_SYMBOL = 'UNUM_DIGIT_SYMBOL';
+ const MINUS_SIGN_SYMBOL = 'UNUM_MINUS_SIGN_SYMBOL';
+ const PLUS_SIGN_SYMBOL = 'UNUM_PLUS_SIGN_SYMBOL';
+ const CURRENCY_SYMBOL = 'UNUM_CURRENCY_SYMBOL';
+ const INTL_CURRENCY_SYMBOL = 'UNUM_INTL_CURRENCY_SYMBOL';
+ const MONETARY_SEPARATOR_SYMBOL = 'UNUM_MONETARY_SEPARATOR_SYMBOL';
+ const EXPONENTIAL_SYMBOL = 'UNUM_EXPONENTIAL_SYMBOL';
+ const PERMILL_SYMBOL = 'UNUM_PERMILL_SYMBOL';
+ const PAD_ESCAPE_SYMBOL = 'UNUM_PAD_ESCAPE_SYMBOL';
+ const INFINITY_SYMBOL = 'UNUM_INFINITY_SYMBOL';
+ const NAN_SYMBOL = 'UNUM_NAN_SYMBOL';
+ const SIGNIFICANT_DIGIT_SYMBOL = 'UNUM_SIGNIFICANT_DIGIT_SYMBOL';
+ const MONETARY_GROUPING_SEPARATOR_SYMBOL = 'UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL';
+
+ /**
+ * Create a number formatter
+ *
+ * Creates a number formatter from locale and pattern. This formatter would be used to
+ * format or parse numbers.
+ *
+ * @param integer $style Style of the formatting, one of the UNumberFormatStyle constants
+ * @param string $locale Locale in which the number would be formatted
+ * @param [string] $pattern Pattern string in case chose style requires pattern
+ * @return NumberFormatter
+ */
+ public function __construct($locale, $style, $pattern = null) {}
+
+ /**
+ * Create a number formatter
+ *
+ * Creates a number formatter from locale and pattern. This formatter would be used to
+ * format or parse numbers.
+ *
+ * This method is useful when you prefer just to get null on error,
+ * as if you called numfmt_create().
+ *
+ * @param integer $style Style of the formatting, one of the UNumberFormatStyle constants
+ * @param string $locale Locale in which the number would be formatted
+ * @param [string] $pattern Pattern string in case chose style requires pattern
+ * @return NumberFormatter
+ * @see __construct
+ * @see numfmt_create
+ */
+ public static function create($locale, $style, $pattern = null) {}
+
+ /**
+ * Format a number according to current formatting rules.
+ *
+ * If the type is not specified, the type is derived from the $number parameter. I.e., if it's
+ * integer then INT32 would be chosen on 32-bit, INT64 on 64-bit, if it's double, DOUBLE would be
+ * chosen. It is possible to format 64-bit number on 32-bit machine by passing it as double and using
+ * TYPE_INT64.
+ * When formatting currency, default formatter's currency code is used.
+ *
+ * @param integer|double $number Number to format
+ * @param [integer] $type Type of the formatting - one of TYPE constants. If not specified, default for the type.
+ * @return string formatted number
+ */
+ public function format($number, $type = 0) {}
+
+ /**
+ * Parse a number according to current formatting rules.
+ *
+ * @param string $string String to parse
+ * @param [integer] $type Type of the formatting - one of TYPE constants.
+ * TYPE_DOUBLE is used by default.
+ * @param [integer] $position On input, the position to start parsing, default is 0;
+ * on output, moved to after the last successfully parse character;
+ * on parse failure, does not change.
+ * @return integer|double|false Parsed number, false if parsing failed
+ */
+ public function parse($string, $type, &$position) {}
+
+ /**
+ * Format number as currency.
+ *
+ * Uses user-defined currency string.
+ *
+ * @param double $number Number to format
+ * @param string $currency 3-letter currency code (ISO 4217) to use in format
+ */
+ public function formatCurrency($number, $currency) {}
+
+ /**
+ * Parse currency string
+ *
+ * This parser would use parseCurrency API string to parse currency string. The format is defined by the
+ * formatter, returns both number and currency code.
+ *
+ * @param string $string String to parse
+ * @param string $currency Parameter to return parsed currency code
+ * @param [integer] $position On input, the position within text to match, default is 0;
+ * on output, the position after the last matched character;
+ * on parse failure, does not change.
+ * @return double currency number
+ */
+ public function parseCurrency($string, &$currency, &$position) {}
+
+ /**
+ * Set formatter attribute.
+ *
+ * This function is used to set any of the formatter attributes. Example:
+ *
+ * $formatter->setAttribute(NumberFormat::FORMAT_WIDTH, 10);
+ *
+ * @param integer $attr One of UNumberFormatAttribute constants
+ * @param integer|double $value Value of the attribute
+ * @return false if attribute is unknown or can not be set, true otherwise
+ */
+ public function setAttribute($attr, $value) {}
+ /**
+ * Set formatter attribute.
+ *
+ * This function is used to set any of the formatter attributes. Example:
+ *
+ * $formatter->setTextAttribute(NumberFormat::POSITIVE_PREFIX, "+");
+ *
+ * @param integer $attr One of UNumberFormatTextAttribute constants
+ * @param string $value Value of the attribute
+ * @return false if attribute is unknown or can not be set, true otherwise
+ */
+ public function setTextAttribute($attr, $value) {}
+ /**
+ * Set formatting symbol.
+ *
+ * Example:
+ *
+ * $formatter->setSymbol(NumberFormat::EXPONENTIAL_SYMBOL, "E");
+ *
+ * @param integer|array $attr One of UNumberFormatSymbol constants or array of symbols, indexed by
+ * these constants
+ * @param string $value Value of the symbol
+ */
+ public function setSymbol($attr, $value) {}
+ /**
+ * Set pattern used by the formatter
+ *
+ * Valid only if the formatter is using pattern and is not rule-based.
+ * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html
+ * Localized patterns are not currently supported.
+ *
+ * @param string $pattern The pattern to be used.
+ * @return boolean false if formatter pattern could not be set, true otherwise
+ */
+ public function setPattern($pattern) {}
+ /**
+ * Get value of the formatter attribute
+ *
+ * @param integer $attr One of UNumberFormatAttribute constants
+ * @return integer|double value of the attribute or false if the value can not be obtained
+ */
+ public function getAttribute($attr) {}
+ /**
+ * Get value of the formatter attribute
+ *
+ * @param integer $attr One of UNumberFormatTextAttribute constants
+ * @return string value of the attribute or false if the value can not be obtained
+ */
+ public function getTextAttribute($attr) {}
+ /**
+ * Get value of the formatter symbol
+ *
+ * @param integer $attr One of UNumberFormatSymbol constants specifying the symbol
+ * @return string|false The symbol value, or false if the value can not be obtained
+ */
+ public function getSymbol($attr) {}
+ /**
+ * Get pattern used by the formatter.
+ *
+ * Gets current state of the formatter as a pattern.
+ * Localized patterns are not currently supported.
+ *
+ * Valid only if the formatter is UNUM_PATTERN_DECIMAL
+ * @return string|false The pattern used by the formatter or false if formatter is of a type
+ * that does not support patterns.
+ */
+ public function getPattern() {}
+ /**
+ * Get the locale for which the formatter was created.
+ *
+ * @param [integer] $type One of ULocDataLocaleType values
+ * @return string locale name
+ */
+ public function getLocale($type = 0) {}
+ /**
+ * Get the error code from last operation
+ *
+ * Returns error code from the last number formatting operation.
+ *
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+ public function getErrorCode() {}
+ /**
+ * Get the error text from the last operation.
+ *
+ * @return string Description of the last occured error.
+ */
+ public public function getErrorMessage() {}
+
+}
+
+/** Now the same as procedural API */
+
+/**
+ * Create a number formatter
+ *
+ * Creates a number formatter from locale and pattern. This formatter would be used to
+ * format or parse numbers.
+ *
+ * @param string $locale Locale in which the number would be formatted
+ * @param integer $style Style of the formatting, one of the UNumberFormatStyle constants
+ * @param [string] $pattern Pattern string in case chose style requires pattern
+ * @return Numberformatter resource NumberFormatter
+ */
+function numfmt_create($locale, $style, $pattern = null) {}
+/**
+ * Format a number according to current formatting rules.
+ *
+ * If the type is not specified, the type is derived from the $number parameter. I.e., if it's
+ * integer then INT32 would be chosen on 32-bit, INT64 on 64-bit, if it's double, DOUBLE would be
+ * chosen. It is possible to format 64-bit number on 32-bit machine by passing it as double and using
+ * TYPE_INT64.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer|double $number Number to format
+ * @param [integer] $type Type of the formatting - one of TYPE constants. If not specified, default for the type.
+ * @return string formatted number
+ */
+function numfmt_format($formatter, $number, $type = null) {}
+/**
+ * Parse a number according to current formatting rules.
+ *
+ * This parser uses DOUBLE type by default. When parsing currency,
+ * default currency definitions are used.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param string $string String to parse
+ * @param [integer] $type Type of the formatting - one of TYPE constants.
+ * @param [integer] $position String position after the end of parsed data.
+ * @return integer|double|false Parsed number, false if parsing failed
+ */
+function numfmt_parse($formatter, $string, $type, &$position) {}
+/**
+ * Format number as currency.
+ *
+ * Uses user-defined currency string.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param double $number Number to format
+ * @param string $currency 3-letter currency code (ISO 4217) to use in format
+ */
+function numfmt_format_currency($formatter, $number, $currency) {}
+/**
+ * Parse currency string
+ *
+ * This parser would use parseCurrency API string to parse currency string. The format is defined by the
+ * formatter, returns both number and currency code.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param string $string String to parse
+ * @param string $currency Parameter to return parsed currency code
+ * @param [integer] $position String position after the end of parsed data.
+ * @return double currency number
+ */
+function numfmt_parse_currency($formatter, $string, &$currency, &$position) {}
+/**
+ * Set formatter attribute.
+ *
+ * This function is used to set any of the formatter attributes. Example:
+ *
+ * numfmt_format_set_attribute($formatter, NumberFormat::FORMAT_WIDTH, 10);
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer $attr One of UNumberFormatAttribute constants
+ * @param integer|double $value Value of the attribute
+ * @return false if attribute is unknown or can not be set, true otherwise
+ */
+function numfmt_set_attribute($formatter, $attribute, $value) {}
+/**
+ * Set formatter attribute.
+ *
+ * This function is used to set any of the formatter attributes. Example:
+ *
+ * numfmt_format_set_text_attribute($formatter, NumberFormat::POSITIVE_PREFIX, "+");
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer $attr One of UNumberFormatTextAttribute constants
+ * @param string $value Value of the attribute
+ * @return false if attribute is unknown or can not be set, true otherwise
+ */
+function numfmt_set_text_attribute($formatter, $attribute, $value) {}
+/**
+ * Set formatting symbol.
+ *
+ * Example:
+ *
+ * $formatter->setSymbol(NumberFormat::EXPONENTIAL_SYMBOL, "E");
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer|array $attr One of UNumberFormatSymbol constants or array of symbols,
+ * indexed by these constants
+ * @param string $value Value of the symbol
+ */
+function numfmt_set_symbol($formatter, $attribute, $value) {}
+/**
+ * Set pattern used by the formatter
+ *
+ * Valid only if the formatter is using pattern and is not rule-based.
+ * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html
+ * Localized patterns are not currently supported.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param string $pattern The pattern to be used.
+ * @return boolean false if formatter pattern could not be set, true otherwise
+ */
+function numfmt_set_pattern($formatter, $pattern) {}
+/**
+ * Get value of the formatter attribute
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer $attribute One of UNumberFormatAttribute constants
+ * @return integer|double value of the attribute or false if the value can not be obtained
+ */
+function numfmt_get_attribute($formatter, $attribute) {}
+/**
+ * Get value of the formatter attribute
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer $attribute One of UNumberFormatTextAttribute constants
+ * @return string value of the attribute or false if the value can not be obtained
+ */
+function numfmt_get_text_attribute($formatter, $attribute) {}
+/**
+ * Get value of the formatter symbol
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param integer $attribute One of UNumberFormatSymbol constants specifying the symbol
+ * @return string|false The symbol value, or false if the value can not be obtained
+ */
+function numfmt_get_symbol($formatter, $attribute) {}
+/**
+ * Get pattern used by the formatter.
+ *
+ * Gets current state of the formatter as a pattern.
+ * Localized patterns are not currently supported.
+ *
+ * Valid only if the formatter is UNUM_PATTERN_DECIMAL
+ * @param NumberFormatter $formatter The formatter resource
+ * @return string|false The pattern used by the formatter or false if formatter is of a type
+ * that does not support patterns.
+ */
+function numfmt_get_pattern($formatter) {}
+/**
+ * Get the locale for which the formatter was created.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @param [integer] $type One of ULocDataLocaleType values
+ * @return string locale name
+ */
+function numfmt_get_locale($formatter, $type = 0) {}
+/**
+ * Get the error code from last operation
+ *
+ * Returns error code from the last number formatting operation.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+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.
+ */
+function numfmt_get_error_message($formatter) {}
+
+?>
diff --git a/ext/intl/doc/grapheme_api.php b/ext/intl/doc/grapheme_api.php
new file mode 100755
index 0000000000..465453fd32
--- /dev/null
+++ b/ext/intl/doc/grapheme_api.php
@@ -0,0 +1,132 @@
+<?php
+#############################################################################
+# Grapheme constants.
+#############################################################################
+
+ /**
+ * grapheme_extract extract_type
+ *
+ */
+ /** Extract the given number of whole grapheme clusters from the string: */
+ define('GRAPHEME_EXTR_COUNT', 0);
+ /** Extract as many whole grapheme clusters as will fit into the given number of bytes: */
+ define('GRAPHEME_EXTR_MAXBYTES', 1);
+ /** Extract whole grapheme clusters up to a maximum number of UTF-8 characters: */
+ define('GRAPHEME_EXTR_MAXCHARS', 2);
+
+
+#############################################################################
+# Grapheme API
+#############################################################################
+
+ /**
+ * Get string length in grapheme units
+ * @param string $input The string being measured for length.
+ * @return int The length of the string on success, and 0 if the string is empty.
+ */
+ function grapheme_strlen($input) {}
+
+ /**
+ * Find position (in grapheme units) of first occurrence of a string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, strpos() will return boolean FALSE.
+ */
+ function grapheme_strpos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of first occurrence of a case-insensitive string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_stripos() will return boolean FALSE.
+ */
+ function grapheme_stripos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of last occurrence of a string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_strrpos() will return boolean FALSE.
+ */
+ function grapheme_strrpos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of last occurrence of a case-insensitive string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_strripos() will return boolean FALSE.
+ */
+ function grapheme_strripos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Return part of a string
+ * @param string $string The input string.
+ * @param int $start If start is non-negative, the returned string will start at the
+ start'th position in string, counting from zero. If start is negative,
+ the returned string will start at the start'th character from the
+ end of string.
+ * @param [int] $length If length is given and is positive, the string returned will contain
+ at most length characters beginning from start (depending on the
+ length of string). If string is less than or equal to start characters
+ long, FALSE will be returned. If length is given and is negative, then
+ that many characters will be omitted from the end of string (after the
+ start position has been calculated when a start is negative). If start
+ denotes a position beyond this truncation, an empty string will be returned.
+ * @return int Returns the extracted part of string.
+ */
+ function grapheme_substr($string, $start, $length = -1) {}
+
+
+ /**
+ * Returns part of haystack string from the first occurrence of needle to the end of haystack.
+ * @param string $haystack The input string.
+ * @param string $needle The string to look for.
+ * @param [boolean] $before_needle If TRUE (the default is FALSE), grapheme_strstr() returns the part of the
+ haystack before the first occurence of the needle.
+ * @return string Returns the portion of string, or FALSE if needle is not found.
+ */
+ function grapheme_strstr($haystack, $needle, $before_needle = FALSE) {}
+
+
+ /**
+ * Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack.
+ * @param string $haystack The input string.
+ * @param string $needle The string to look for.
+ * @param [boolean] $before_needle If TRUE (the default is FALSE), grapheme_strstr() returns the part of the
+ haystack before the first occurence of the needle.
+ * @return string Returns the portion of string, or FALSE if needle is not found.
+ */
+ function grapheme_stristr($haystack, $needle, $before_needle = FALSE) {}
+
+
+ /**
+ * Function to extract a sequence of default grapheme clusters from a text buffer, which must be encoded in UTF-8.
+ * @param string $haystack string to search
+ * @param int $size maximum number of units - based on the $extract_type - to return
+ * @param [int] $extract_type one of GRAPHEME_EXTR_COUNT (default), GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS
+ * @param [int] $start starting position in $haystack in bytes
+ * @param [&int] $next set to next starting position in bytes
+ * @return string A string starting at offset $start containing no more than $size grapheme clusters
+ and ending on a default grapheme cluster boundary.
+ */
+ function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_COUNT, $start = 0, &$next) {}
+
+?>
+
+
diff --git a/ext/intl/doc/locale_api.php b/ext/intl/doc/locale_api.php
new file mode 100755
index 0000000000..b16e36aa55
--- /dev/null
+++ b/ext/intl/doc/locale_api.php
@@ -0,0 +1,432 @@
+<?php
+
+/**
+ * A "Locale" is an identifier used to get language, culture, or regionally-specific
+ * behavior from an API. PHP locales are organized and identified the same
+ * way that the CLDR locales used by ICU (and many vendors of Unix-like operating
+ * systems, the Mac, Java, and so forth) use. Locales are identified using
+ * RFC 4646 language tags (which use hyphen, not underscore) in addition to the
+ * more traditional underscore-using identifiers. Unless otherwise noted
+ * the functions in this class are tolerant of both formats.
+ *
+ * Examples of identifiers include:
+ *
+ * * en-US (English, United States)
+ * * zh-Hant-TW (Chinese, Traditional Script, Taiwan)
+ * * fr-CA, fr-FR (French for Canada and France respectively)
+ *
+ * The Locale class (and related procedural functions) are used to interact
+ * with locale identifiers--to verify that an ID is well-formed, valid,
+ * etc. The extensions used by CLDR in UAX #35 (and inherited by ICU) are
+ * valid and used wherever they would be in ICU normally.
+ *
+ * Locales cannot be instantiated as objects. All of the functions/methods
+ * provided are static.
+ *
+ * * The null or empty string obtains the "root" locale.
+ * The "root" locale is equivalent to "en_US_POSIX" in CLDR.
+ * * Language tags (and thus locale identifiers) are case insensitive. There
+ * exists a canonicalization function to make case match the specification.
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/uloc_8h.html
+ * @see http://www.unicode.org/reports/tr35/
+ *
+ */
+class Locale {
+
+#############################################################################
+# Common constants.
+#############################################################################
+
+ /**
+ * The following static members are used with the getLocale methods of
+ * the various locale affected classes, such as numfmt.
+ */
+ const DEFAULT_LOCALE = default_locale;
+
+ /**
+ * identifiers for the actual locale, valid locale
+ * WARNING:
+ * The values described here are NOT the actual values in PHP code.
+ * They are references to the ICU C definitions, so the line
+ * const ACTUAL_LOCALE = 'ULOC_ACTUAL_LOCALE';
+ * actually means that Locale::ACTUAL_LOCALE is the same as
+ * ULOC_ACTUAL_LOCALE constant in the ICU library.
+ */
+ const ACTUAL_LOCALE = 'ULOC_ACTUAL_LOCALE';
+ const VALID_LOCALE = 'ULOC_VALID_LOCALE';
+
+ /**
+ * Valid locale tag and subtag values
+ */
+ LANG_TAG = "language";
+ EXTLANG_TAG = "extlang";
+ SCRIPT_TAG = "script";
+ REGION_TAG = "region";
+ VARIANT_TAG = "variant";
+ GRANDFATHERED_LANG_TAG = "grandfathered";
+ PRIVATE_TAG = "private";
+
+
+#############################################################################
+# Object-oriented API
+#############################################################################
+
+ /**
+ * Gets the default locale value from the INTL global 'default_locale'
+ * At the PHP initilaization (MINIT) this value is set to
+ * 'intl.default_locale' value from php.ini if that value exists
+ * or from ICU's function uloc_getDefault()
+ * Then onwards picks up changes from setDefault() calls also
+ *
+ * @return string the current runtime locale
+ */
+ public static function getDefault() {}
+
+ /**
+ * sets the default runtime locale to $locale
+ * This changes the value of INTL global 'default_locale'
+ *
+ * @param string $locale is a BCP 47 compliant language tag containing the
+ * locale identifier. UAX #35 extensions are accepted.
+ * @return boolean 'true' if okay, 'false' if an error
+ */
+ public static function setDefault($locale) {}
+
+
+ /**
+ * Gets the primary language for the input locale
+ *
+ * @param string $locale the locale to extract the primary language code from
+ * @return string the language code associated with the language
+ * or null in case of error.
+ */
+ public static function getPrimaryLanguage($locale) {}
+
+
+ /**
+ * Gets the script for the input locale
+ *
+ * @param string $locale the locale to extract the script code from
+ * @return string the script subtag for the locale or null if not present
+ */
+ public static function getScript($locale) {}
+
+
+ /**
+ * Gets the region for the input locale
+ *
+ * @param string $locale the locale to extract the region code from
+ * @return string the region subtag for the locale or null if not present
+ */
+ public static function getRegion($locale) {}
+
+
+ /**
+ * Gets the variants for the input locale
+ *
+ * @param string $locale the locale to extract the variants from
+ * @return array the array containing the list of all variants
+ * subtag for the locale or null if not present
+ */
+ public static function getAllVariants($locale) {}
+
+
+ /**
+ * Gets the keywords for the input locale
+ *
+ * @param string $locale the locale to extract the keywords from
+ * @return array associative array containing the keyword-value pairs for this locale
+ */
+ public static function getKeywords($locale) {}
+
+
+ /**
+ * Returns an appropriately localized display name for the input locale
+ *
+ * @param string $locale the locale to return a displayname for
+ * @param [string] $in_locale optional format locale
+ * If is 'null' then the default locale is used.
+ * @return string display name of the locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function getDisplayName($locale, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for language of the input locale
+ *
+ * @param string $locale the locale to return a display language for
+ * @param [string] $in_locale optional format locale to use to display the language name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the language for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function getDisplayLanguage($lang, $in_locale = null) {}
+
+ /**
+ * Returns an appropriately localized display name for script of the input locale
+ *
+ * @param string $locale the locale to return a display script for
+ * @param [string] $in_locale optional format locale to use to display the script name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the script for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function getDisplayScript($script, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for region of the input locale
+ *
+ * @param string $locale the locale to return a display region for
+ * @param [string] $in_locale optional format locale to use to display the region name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the region for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function getDisplayRegion($region, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for variants of the input locale
+ *
+ * @param string $locale the locale to return a display variant for
+ * @param [string] $in_locale optional format locale to use to display the variant name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the variant for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function getDisplayVariant($variant, $in_locale = null) {}
+
+
+ /**
+ * Checks if a $langtag filter matches with $locale according to
+ * RFC 4647's basic filtering algorithm
+ *
+ * @param string $langtag the language tag to check
+ * @param string $locale the language range to check against
+ * @return boolean 'true' if $locale matches $langtag 'false' otherwise
+ */
+ public static function filterMatches($langtag, $locale) {}
+
+ /**
+ * Searchs the items in $langtag for the best match to the language
+ * range specified in $locale according to RFC 4647's lookup algorithm.
+ *
+ * @param array $langtag an array containing a list of language tags to compare
+ * to $locale
+ * @param string $locale the locale to use as the language range when matching
+ * @param string $default the locale to use if no match is found
+ * @return string closest matching language tag, $default,
+ * or empty string
+ */
+ public static function lookup(array $langtag, $locale, $default = null) {}
+
+
+ /**
+ * Returns a correctly ordered and delimited locale ID
+ *
+ * @param array $subtags an array containing a list of key-value pairs, where
+ * the keys identify the particular locale ID subtags,
+ * and the values are the associated subtag values.
+ *
+ * @return string the corresponding locale identifier.
+ */
+ public static function composeLocale(array $subtags) {}
+
+
+ /**
+ * Returns a key-value array of locale ID subtag elements.
+ *
+ * @param string $locale the locale to extract the subtag array from
+ *
+ * @return array $subtags an array containing a list of key-value pairs, where
+ * the keys identify the particular locale ID subtags,
+ * and the values are the associated subtag values.
+ */
+ public static function parseLocale($locale) {}
+
+}
+
+#############################################################################
+# Procedural API
+#############################################################################
+
+
+ /**
+ * Gets the default locale value from the INTL global 'default_locale'
+ * At the PHP initilaization (MINIT) this value is set to
+ * 'intl.default_locale' value from php.ini if that value exists
+ * or from ICU's function uloc_getDefault()
+ * Then onwards picks up changes from setDefault() calls also
+ *
+ * @return string the current runtime locale
+ */
+ public static function locale_get_default() {}
+
+ /**
+ * sets the default runtime locale to $locale
+ * This changes the value of INTL global 'default_locale'
+ *
+ * @param string $locale is a BCP 47 compliant language tag containing the
+ * locale identifier. UAX #35 extensions are accepted.
+ * @return boolean 'true' if okay, 'false' if an error
+ */
+ public static function locale_set_default($locale) {}
+
+
+ /**
+ * Gets the primary language for the input locale
+ *
+ * @param string $locale the locale to extract the primary language code from
+ * @return string the language code associated with the language
+ * or null in case of error.
+ */
+ public static function locale_get_primary_language($locale) {}
+
+
+ /**
+ * Gets the script for the input locale
+ *
+ * @param string $locale the locale to extract the script code from
+ * @return string the script subtag for the locale or null if not present
+ */
+ public static function locale_get_script($locale) {}
+
+
+ /**
+ * Gets the region for the input locale
+ *
+ * @param string $locale the locale to extract the region code from
+ * @return string the region subtag for the locale or null if not present
+ */
+ public static function locale_get_region($locale) {}
+
+
+ /**
+ * Gets the variants for the input locale
+ *
+ * @param string $locale the locale to extract the variants from
+ * @return array the array containing the list of all variants
+ * subtag for the locale or null if not present
+ */
+ public static function locale_get_all_variants($locale) {}
+
+
+ /**
+ * Gets the keywords for the input locale
+ *
+ * @param string $locale the locale to extract the keywords from
+ * @return array associative array containing the keyword-value pairs for this locale
+ */
+ public static function locale_get_keywords($locale) {}
+
+
+ /**
+ * Returns an appropriately localized display name for the input locale
+ *
+ * @param string $locale the locale to return a displayname for
+ * @param [string] $in_locale optional format locale
+ * If is 'null' then the default locale is used.
+ * @return string display name of the locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function locale_get_display_name($locale, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for language of the input locale
+ *
+ * @param string $locale the locale to return a display language for
+ * @param [string] $in_locale optional format locale to use to display the language name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the language for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function locale_get_display_language($lang, $in_locale = null) {}
+
+ /**
+ * Returns an appropriately localized display name for script of the input locale
+ *
+ * @param string $locale the locale to return a display script for
+ * @param [string] $in_locale optional format locale to use to display the script name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the script for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function locale_get_display_script($script, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for region of the input locale
+ *
+ * @param string $locale the locale to return a display region for
+ * @param [string] $in_locale optional format locale to use to display the region name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the region for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function locale_get_display_region($region, $in_locale = null) {}
+
+
+ /**
+ * Returns an appropriately localized display name for variants of the input locale
+ *
+ * @param string $locale the locale to return a display variant for
+ * @param [string] $in_locale optional format locale to use to display the variant name
+ * If is 'null' then the default locale is used.
+ * @return string display name of the variant for the $locale in the format
+ * appropriate for $in_locale.
+ */
+ public static function locale_get_display_variant($variant, $in_locale = null) {}
+
+
+ /**
+ * Checks if a $langtag filter matches with $locale according to
+ * RFC 4647's basic filtering algorithm
+ *
+ * @param string $langtag the language tag to check
+ * @param string $locale the language range to check against
+ * @return boolean 'true' if $locale matches $langtag 'false' otherwise
+ */
+ public static function locale_filter_matches($langtag, $locale) {}
+
+ /**
+ * Searchs the items in $langtag for the best match to the language
+ * range specified in $locale according to RFC 4647's lookup algorithm.
+ *
+ * @param array $langtag an array containing a list of language tags to compare
+ * to $locale
+ * @param string $locale the locale to use as the language range when matching
+ * @param string $default the locale to use if no match is found
+ * @return string closest matching language tag, $default,
+ * or empty string
+ */
+ public static function locale_lookup(array $langtag, $locale, $default = null) {}
+
+
+ /**
+ * Returns a correctly ordered and delimited locale ID
+ *
+ * @param array $subtags an array containing a list of key-value pairs, where
+ * the keys identify the particular locale ID subtags,
+ * and the values are the associated subtag values.
+ *
+ * @return string the corresponding locale identifier.
+ */
+ public static function locale_compose_locale(array $subtags) {}
+
+
+ /**
+ * Returns a key-value array of locale ID subtag elements.
+ *
+ * @param string $locale the locale to extract the subtag array from
+ *
+ * @return array $subtags an array containing a list of key-value pairs, where
+ * the keys identify the particular locale ID subtags,
+ * and the values are the associated subtag values.
+ */
+ public static function locale_parse_locale($locale) {}
+
+?>
diff --git a/ext/intl/doc/msgfmt_api.php b/ext/intl/doc/msgfmt_api.php
new file mode 100755
index 0000000000..5d178f1273
--- /dev/null
+++ b/ext/intl/doc/msgfmt_api.php
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Message formatter class.
+ *
+ * Message Format provides for runtime formatting of messages in a manner
+ * somewhat similar to sprintf. The pattern string has its component parts
+ * replaced in a locale-sensitive manner using items in the arguments array.
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/umsg_8h.html
+ *
+ */
+class MessageFormatter {
+
+ /**
+ * Constructs a new Message Formatter
+ *
+ * @param string $locale the locale to use when formatting arguments
+ * @param string $pattern the pattern string to stick arguments into
+ */
+ public function __construct($locale, $pattern) {}
+
+ /**
+ * Constructs a new Message Formatter
+ *
+ * @param string $locale the locale to use when formatting arguments
+ * @param string $pattern the pattern string to stick arguments into
+ */
+ public static function create($locale, $pattern) {}
+
+ /**
+ * Format the message
+ * @param array $args arguments to insert into the pattern string
+ * @return string the formatted string, or false if an error ocurred
+ */
+ public function format($args) {}
+
+ /**
+ * Parse input string and returns any extracted items as an array
+ *
+ * $error will contain any error code. If an error occurs, $parse_pos contains
+ * the position of the error.
+ *
+ * @param string $value string to parse for items
+ * @return array array containing items extracted
+ *
+ */
+ public function parse($value) {}
+
+ /**
+ * Inserts the items in $args into $pattern, formatting them
+ * according to $locale. This is the static implementation.
+ *
+ * @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
+ */
+ public static function formatMessage($locale, $pattern, $args) {}
+
+ /**
+ * parses input string and returns any extracted items as an array
+ *
+ * $error will contain any error code. If an error occurs, $parse_pos contains
+ * the position of the error.
+ *
+ * @param string $locale the locale to use when formatting numbers and dates and suchlike
+ * @param string $value string to parse for items
+ * @return array array containing items extracted
+ *
+ */
+ public static function parseMessage($locale, $value) {}
+
+ /**
+ * Get the pattern used by the formatter
+ *
+ * @return string the pattern string for this message formatter
+ */
+ public function getPattern() {}
+
+ /**
+ * Set the pattern used by the formatter
+ *
+ * @param string $pattern the pattern string to use in this message formatter
+ * @return boolean 'true' if successful, 'false' if an error
+ */
+ public function setPattern($pattern) {}
+
+ /**
+ * Get the error code from last operation
+ *
+ * Returns error code from the last number formatting operation.
+ *
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+ public function getErrorCode() {}
+ /**
+ * Get the error text from the last operation.
+ *
+ * @return string Description of the last error.
+ */
+ public function getErrorMessage() {}
+ /**
+ * Get the locale for which the formatter was created.
+ *
+ * @return string locale name
+ */
+ public function getLocale() {}
+}
+
+ /** Now the same as procedural API */
+
+
+ /**
+ * Constructs a new Message Formatter
+ *
+ * @param string $locale the locale to use when formatting arguments
+ * @param string $pattern the pattern string to stick arguments into
+ * @return MessageFormatter formatter object
+ */
+ function msgfmt_create($locale, $pattern) {}
+
+ /**
+ * Format the message
+ * @param MessageFormatter $fmt The message formatter
+ * @param array $args arguments to insert into the pattern string
+ * @return string the formatted string, or false if an error ocurred
+ */
+ function msgfmt_format($fmt, $args) {}
+
+ /**
+ * parses input string and returns any extracted items as an array
+ *
+ * $error will contain any error code. If an error occurs, $parse_pos contains
+ * the position of the error.
+ *
+ * @param MessageFormatter $fmt The message formatter
+ * @param string $value string to parse for items
+ * @return array array containing items extracted
+ *
+ */
+ function msgfmt_parse($fmt, $value) {}
+
+ /**
+ * Inserts the items in $args into $pattern, formatting them
+ * according to $locale. This is the static implementation.
+ *
+ * @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
+ */
+ function msgfmt_format_message($locale, $pattern, $args) {}
+
+ /**
+ * parses input string and returns any extracted items as an array
+ *
+ * $error will contain any error code. If an error occurs, $parse_pos contains
+ * the position of the error.
+ *
+ * @param string $locale the locale to use when formatting numbers and dates and suchlike
+ * @param string $value string to parse for items
+ * @return array array containing items extracted
+ *
+ */
+ function msgfmt_parse_message($locale, $value) {}
+
+ /**
+ * Get the pattern used by the formatter
+ *
+ * @param MessageFormatter $fmt The message formatter
+ * @return string the pattern string for this message formatter
+ */
+ function msgfmt_get_pattern($fmt) {}
+
+ /**
+ * Set the pattern used by the formatter
+ *
+ * @param MessageFormatter $fmt The message formatter
+ * @param string $pattern the pattern string to use in this message formatter
+ * @return boolean 'true' if successful, 'false' if an error
+ */
+ function msgfmt_set_pattern($fmt, $pattern) {}
+
+ /**
+ * Get the error code from last operation
+ *
+ * Returns error code from the last number formatting operation.
+ *
+ * @param MessageFormatter $fmt The message formatter
+ * @return integer the error code, one of UErrorCode values. Initial value is U_ZERO_ERROR.
+ */
+ function msgfmt_get_error_code($fmt) {}
+ /**
+ * Get the error text from the last operation.
+ *
+ * @param MessageFormatter $fmt The message formatter
+ * @return string Description of the last error.
+ */
+ function msgfmt_get_error_message($fmt) {}
+
+ /**
+ * Get the locale for which the formatter was created.
+ *
+ * @param NumberFormatter $formatter The formatter resource
+ * @return string locale name
+ */
+ function msgfmt_get_locale($formatter) {}
+?>
diff --git a/ext/intl/doc/normalizer_api.php b/ext/intl/doc/normalizer_api.php
new file mode 100755
index 0000000000..a9bb566e67
--- /dev/null
+++ b/ext/intl/doc/normalizer_api.php
@@ -0,0 +1,92 @@
+<?php
+
+#############################################################################
+# Object-oriented API
+#############################################################################
+
+/**
+ * Normalizer class.
+ *
+ * Normalizer provides access to Unicode normalization of strings. This class consists
+ * only of static methods. The iterator interface to normalizer is rarely used, so is
+ * not provided here.
+ *
+ * Example:
+ * <code>
+ *
+ * </code>
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/unorm_8h.html
+ * @see http://www.icu-project.org/apiref/icu4c/classNormalizer.html
+ *
+ */
+class Normalizer {
+#############################################################################
+# Common constants.
+#############################################################################
+
+ /**
+ * Valid normalization form values.
+ *
+ * @see Normalizer::normalize()
+ * @see Normalizer::isNormalize()
+ * @see normalizer_normalize()
+ * @see normalizer_is_normalized()
+ */
+ const NONE = 1;
+ /** Canonical decomposition. */
+ const NFD = 2;
+ const FORM_D = NFD;
+ /** Compatibility decomposition. */
+ const NFKD = 3;
+ const FORM_KD = NFKD;
+ /** Canonical decomposition followed by canonical composition. */
+ const NFC = 4;
+ const FORM_C = NFC;
+ /** Compatibility decomposition followed by canonical composition. */
+ const NFKC =5;
+ const FORM_KC = NFKC;
+
+
+ /**
+ * Normalizes the input provided and returns the normalized string
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return string The normalized string or null if an error occurred.
+ */
+ public static function normalize($input, $form = Normalizer::FORM_C) {}
+
+ /**
+ * Checks if the provided string is already in the specified normalization form.
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return boolean True if normalized, false otherwise or if there is an error
+ */
+ public static function isNormalized($input, $form = Normalizer::FORM_C) {}
+
+}
+
+#############################################################################
+# Procedural API
+#############################################################################
+
+ /**
+ * Normalizes the input provided and returns the normalized string
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return string The normalized string or null if an error occurred.
+ */
+ function normalizer_normalize($input, $form = Normalizer::FORM_C) {}
+
+ /**
+ * Checks if the provided string is already in the specified normalization form.
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return boolean True if normalized, false otherwise or if there an error
+ */
+ function normalizer_is_normalized($input, $form = Normalizer::FORM_C) {}
+
+
+?>
+
+
diff --git a/ext/intl/formatter/formatter.c b/ext/intl/formatter/formatter.c
new file mode 100755
index 0000000000..036974dc5b
--- /dev/null
+++ b/ext/intl/formatter/formatter.c
@@ -0,0 +1,152 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/utypes.h>
+#include <unicode/unum.h>
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+
+#if U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM <= 4
+#define UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL 17
+#endif
+
+/* {{{ formatter_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void formatter_register_constants( INIT_FUNC_ARGS )
+{
+ if( NumberFormatter_ce_ptr == NULL) {
+ zend_error(E_ERROR, "NumberFormatter class not defined");
+ }
+
+ #define FORMATTER_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define FORMATTER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( NumberFormatter_ce_ptr, ZEND_STRS( #x ) - 1, UNUM_##x TSRMLS_CC );
+ #define FORMATTER_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( NumberFormatter_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ // UNumberFormatStyle constants
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_DECIMAL );
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY );
+ FORMATTER_EXPOSE_CLASS_CONST( PERCENT );
+ FORMATTER_EXPOSE_CLASS_CONST( SCIENTIFIC );
+ FORMATTER_EXPOSE_CLASS_CONST( SPELLOUT );
+ FORMATTER_EXPOSE_CLASS_CONST( ORDINAL );
+ FORMATTER_EXPOSE_CLASS_CONST( DURATION );
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_RULEBASED );
+ FORMATTER_EXPOSE_CLASS_CONST( IGNORE );
+
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "DEFAULT_STYLE", UNUM_DEFAULT );
+
+/* workaround for ICU bug */
+#if U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM < 8
+#define UNUM_ROUND_HALFEVEN UNUM_FOUND_HALFEVEN
+#endif
+
+ // UNumberFormatRoundingMode
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_CEILING );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_FLOOR );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_DOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_UP );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFEVEN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFDOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFUP );
+
+ // UNumberFormatPadPosition
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_BEFORE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_AFTER_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_BEFORE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_AFTER_SUFFIX );
+
+ // UNumberFormatAttribute
+ FORMATTER_EXPOSE_CLASS_CONST( PARSE_INT_ONLY );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_USED );
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL_ALWAYS_SHOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MULTIPLIER );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_SIZE );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUNDING_MODE );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUNDING_INCREMENT );
+ FORMATTER_EXPOSE_CLASS_CONST( FORMAT_WIDTH );
+ FORMATTER_EXPOSE_CLASS_CONST( PADDING_POSITION );
+ FORMATTER_EXPOSE_CLASS_CONST( SECONDARY_GROUPING_SIZE );
+ FORMATTER_EXPOSE_CLASS_CONST( SIGNIFICANT_DIGITS_USED );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_SIGNIFICANT_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_SIGNIFICANT_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( LENIENT_PARSE );
+
+ // UNumberFormatTextAttribute
+ FORMATTER_EXPOSE_CLASS_CONST( POSITIVE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( POSITIVE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( NEGATIVE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( NEGATIVE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PADDING_CHARACTER );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY_CODE );
+ FORMATTER_EXPOSE_CLASS_CONST( DEFAULT_RULESET );
+ FORMATTER_EXPOSE_CLASS_CONST( PUBLIC_RULESETS );
+
+ // UNumberFormatSymbol
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PERCENT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( ZERO_DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MINUS_SIGN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PLUS_SIGN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( INTL_CURRENCY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MONETARY_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( EXPONENTIAL_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PERMILL_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_ESCAPE_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( INFINITY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( NAN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( SIGNIFICANT_DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MONETARY_GROUPING_SEPARATOR_SYMBOL );
+
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_DEFAULT", FORMAT_TYPE_DEFAULT );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_INT32", FORMAT_TYPE_INT32 );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_INT64", FORMAT_TYPE_INT64 );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_DOUBLE", FORMAT_TYPE_DOUBLE );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_CURRENCY", FORMAT_TYPE_CURRENCY );
+
+ #undef FORMATTER_EXPOSE_CUSTOM_CLASS_CONST
+ #undef FORMATTER_EXPOSE_CLASS_CONST
+ #undef FORMATTER_EXPOSE_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/formatter/formatter.h b/ext/intl/formatter/formatter.h
new file mode 100755
index 0000000000..1f9ebead8e
--- /dev/null
+++ b/ext/intl/formatter/formatter.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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_FORMATTER_H
+#define FORMATTER_FORMATTER_H
+
+#include <php.h>
+
+void formatter_register_constants( INIT_FUNC_ARGS );
+
+#endif // FORMATTER_FORMATTER_H
diff --git a/ext/intl/formatter/formatter_attr.c b/ext/intl/formatter/formatter_attr.c
new file mode 100755
index 0000000000..40f39e283e
--- /dev/null
+++ b/ext/intl/formatter/formatter_attr.c
@@ -0,0 +1,443 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_attr.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+
+/* {{{ proto mixed NumberFormatter::getAttribute( int $attr )
+ * Get formatter attribute value. }}} */
+/* {{{ proto mixed numfmt_get_attribute( NumberFormatter $nf, int $attr )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_get_attribute )
+{
+ long attribute, value;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ switch(attribute) {
+ case UNUM_PARSE_INT_ONLY:
+ case UNUM_GROUPING_USED:
+ case UNUM_DECIMAL_ALWAYS_SHOWN:
+ case UNUM_MAX_INTEGER_DIGITS:
+ case UNUM_MIN_INTEGER_DIGITS:
+ case UNUM_INTEGER_DIGITS:
+ case UNUM_MAX_FRACTION_DIGITS:
+ case UNUM_MIN_FRACTION_DIGITS:
+ case UNUM_FRACTION_DIGITS:
+ case UNUM_MULTIPLIER:
+ case UNUM_GROUPING_SIZE:
+ case UNUM_ROUNDING_MODE:
+ case UNUM_FORMAT_WIDTH:
+ case UNUM_PADDING_POSITION:
+ case UNUM_SECONDARY_GROUPING_SIZE:
+ case UNUM_SIGNIFICANT_DIGITS_USED:
+ case UNUM_MIN_SIGNIFICANT_DIGITS:
+ case UNUM_MAX_SIGNIFICANT_DIGITS:
+ case UNUM_LENIENT_PARSE:
+ value = unum_getAttribute(FORMATTER_OBJECT(nfo), attribute);
+ if(value == -1) {
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ } else {
+ RETVAL_LONG(value);
+ }
+ break;
+ case UNUM_ROUNDING_INCREMENT:
+ {
+ double value = unum_getDoubleAttribute(FORMATTER_OBJECT(nfo), attribute);
+ if(value == -1) {
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ } else {
+ RETVAL_DOUBLE(value);
+ }
+ }
+ break;
+ default:
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ break;
+ }
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getTextAttribute( int $attr )
+ * Get formatter attribute value. }}} */
+/* {{{ proto string numfmt_get_text_attribute( NumberFormatter $nf, int $attr )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_get_text_attribute )
+{
+ long attribute;
+ UChar value_buf[64];
+ int value_buf_size = USIZE( value_buf );
+ UChar* value = value_buf;
+ int length = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_text_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) );
+ if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= value_buf_size) {
+ ++length; // to avoid U_STRING_NOT_TERMINATED_WARNING
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setAttribute( int $attr, mixed $value )
+ * Get formatter attribute value. }}} */
+/* {{{ proto bool numfmt_set_attribute( NumberFormatter $nf, int $attr, mixed $value )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_set_attribute )
+{
+ long attribute;
+ zval **value;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OlZ",
+ &object, NumberFormatter_ce_ptr, &attribute, &value ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ switch(attribute) {
+ case UNUM_PARSE_INT_ONLY:
+ case UNUM_GROUPING_USED:
+ case UNUM_DECIMAL_ALWAYS_SHOWN:
+ case UNUM_MAX_INTEGER_DIGITS:
+ case UNUM_MIN_INTEGER_DIGITS:
+ case UNUM_INTEGER_DIGITS:
+ case UNUM_MAX_FRACTION_DIGITS:
+ case UNUM_MIN_FRACTION_DIGITS:
+ case UNUM_FRACTION_DIGITS:
+ case UNUM_MULTIPLIER:
+ case UNUM_GROUPING_SIZE:
+ case UNUM_ROUNDING_MODE:
+ case UNUM_FORMAT_WIDTH:
+ case UNUM_PADDING_POSITION:
+ case UNUM_SECONDARY_GROUPING_SIZE:
+ case UNUM_SIGNIFICANT_DIGITS_USED:
+ case UNUM_MIN_SIGNIFICANT_DIGITS:
+ case UNUM_MAX_SIGNIFICANT_DIGITS:
+ case UNUM_LENIENT_PARSE:
+ convert_to_long_ex(value);
+ unum_setAttribute(FORMATTER_OBJECT(nfo), attribute, Z_LVAL_PP(value));
+ break;
+ case UNUM_ROUNDING_INCREMENT:
+ convert_to_double_ex(value);
+ unum_setDoubleAttribute(FORMATTER_OBJECT(nfo), attribute, Z_DVAL_PP(value));
+ break;
+ default:
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ break;
+ }
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting attribute value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setTextAttribute( int $attr, string $value )
+ * Get formatter attribute value. }}} */
+/* {{{ proto bool numfmt_set_text_attribute( NumberFormatter $nf, int $attr, string $value )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_set_text_attribute )
+{
+ int slength = 0;
+ UChar *svalue = NULL;
+ long attribute;
+ char *value;
+ int len;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols",
+ &object, NumberFormatter_ce_ptr, &attribute, &value, &len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_text_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert given attribute value to UTF-16.
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting attribute value to UTF-16" );
+
+ // Actually set new attribute value.
+ unum_setTextAttribute(FORMATTER_OBJECT(nfo), attribute, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
+ efree(svalue);
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getSymbol( int $attr )
+ * Get formatter symbol value. }}} */
+/* {{{ proto string numfmt_get_symbol( NumberFormatter $nf, int $attr )
+ * Get formatter symbol value.
+ */
+PHP_FUNCTION( numfmt_get_symbol )
+{
+ long symbol;
+ UChar value_buf[4];
+ UChar *value = value_buf;
+ int length = USIZE(value);
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, NumberFormatter_ce_ptr, &symbol ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_symbol: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo));
+ if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value )) {
+ ++length; // to avoid U_STRING_NOT_TERMINATED_WARNING
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting symbol value" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value_buf != value ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setSymbol( int $attr, string $symbol )
+ * Set formatter symbol value. }}} */
+/* {{{ proto bool numfmt_set_symbol( NumberFormatter $nf, int $attr, string $symbol )
+ * Set formatter symbol value.
+ */
+PHP_FUNCTION( numfmt_set_symbol )
+{
+ long symbol;
+ char* value = NULL;
+ int value_len = 0;
+ UChar* svalue = 0;
+ int slength = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols",
+ &object, NumberFormatter_ce_ptr, &symbol, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_symbol: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert given symbol to UTF-16.
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting symbol value to UTF-16" );
+
+ // Actually set the symbol.
+ unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
+ efree(svalue);
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string numfmt_get_pattern( NumberFormatter $nf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( numfmt_get_pattern )
+{
+ UChar value_buf[64];
+ int length = USIZE( value_buf );
+ UChar* value = value_buf;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_pattern: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ length = unum_toPattern(FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
+ ++length; // to avoid U_STRING_NOT_TERMINATED_WARNING
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_toPattern( FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting formatter pattern" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setPattern( string $pattern )
+ * Set formatter pattern. }}} */
+/* {{{ proto bool numfmt_set_pattern( NumberFormatter $nf, string $pattern )
+ * Set formatter pattern.
+ */
+PHP_FUNCTION( numfmt_set_pattern )
+{
+ char* value = NULL;
+ int value_len = 0;
+ int slength = 0;
+ UChar* svalue = NULL;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, NumberFormatter_ce_ptr, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_pattern: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert given pattern to UTF-16.
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting pattern to UTF-16" );
+
+ // TODO: add parse error information
+ unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ efree(svalue);
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting pattern value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getLocale([int type])
+ * Get formatter locale. }}} */
+/* {{{ proto string numfmt_get_locale( NumberFormatter $nf[, int type] )
+ * Get formatter locale.
+ */
+PHP_FUNCTION( numfmt_get_locale )
+{
+ long type = ULOC_ACTUAL_LOCALE;
+ char* loc;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l",
+ &object, NumberFormatter_ce_ptr, &type ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ loc = (char *)unum_getLocaleByType(FORMATTER_OBJECT(nfo), type, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting locale" );
+ RETURN_STRING(loc, 1);
+}
+/* }}} */
+
+/*
+ * 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/formatter/formatter_attr.h b/ext/intl/formatter/formatter_attr.h
new file mode 100755
index 0000000000..c4bc94ce5f
--- /dev/null
+++ b/ext/intl/formatter/formatter_attr.h
@@ -0,0 +1,32 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_ATTR_H
+#define FORMATTER_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_set_attribute );
+PHP_FUNCTION( numfmt_get_attribute );
+PHP_FUNCTION( numfmt_set_text_attribute );
+PHP_FUNCTION( numfmt_get_text_attribute );
+PHP_FUNCTION( numfmt_set_symbol );
+PHP_FUNCTION( numfmt_get_symbol );
+PHP_FUNCTION( numfmt_set_pattern );
+PHP_FUNCTION( numfmt_get_pattern );
+PHP_FUNCTION( numfmt_get_locale );
+
+#endif // FORMATTER_ATTR_H
diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c
new file mode 100755
index 0000000000..776b9d162b
--- /dev/null
+++ b/ext/intl/formatter/formatter_class.c
@@ -0,0 +1,147 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <unicode/unum.h>
+
+#include "formatter_class.h"
+#include "php_intl.h"
+#include "formatter_data.h"
+#include "formatter_format.h"
+#include "formatter_parse.h"
+#include "formatter_main.h"
+#include "formatter_attr.h"
+
+zend_class_entry *NumberFormatter_ce_ptr = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// Auxiliary functions needed by objects of 'NumberFormatter' class
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ NumberFormatter_objects_dtor */
+static void NumberFormatter_object_dtor(
+ void *object,
+ zend_object_handle handle TSRMLS_DC )
+{
+ zend_objects_destroy_object( object, handle TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ NumberFormatter_objects_free */
+void NumberFormatter_object_free( zend_object *object TSRMLS_DC )
+{
+ NumberFormatter_object* nfo = (NumberFormatter_object*)object;
+
+ zend_object_std_dtor( &nfo->zo TSRMLS_CC );
+
+ formatter_data_free( &nfo->nf_data TSRMLS_CC );
+
+ efree( nfo );
+}
+/* }}} */
+
+/* {{{ NumberFormatter_object_create */
+zend_object_value NumberFormatter_object_create(
+ zend_class_entry *ce TSRMLS_DC )
+{
+ zend_object_value retval;
+ NumberFormatter_object* intern;
+
+ intern = ecalloc( 1, sizeof(NumberFormatter_object) );
+ formatter_data_init( &intern->nf_data TSRMLS_CC );
+ zend_object_std_init( &intern->zo, ce TSRMLS_CC );
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ NumberFormatter_object_dtor,
+ (zend_objects_free_object_storage_t)NumberFormatter_object_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = zend_get_std_object_handlers();
+
+ return retval;
+}
+/* }}} */
+
+/////////////////////////////////////////////////////////////////////////////
+// 'NumberFormatter' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ NumberFormatter_class_functions
+ * Every 'NumberFormatter' class method has an entry in this table
+ */
+static ZEND_BEGIN_ARG_INFO_EX( number_parse_arginfo, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, string )
+ ZEND_ARG_INFO( 0, type )
+ ZEND_ARG_INFO( 1, position )
+ZEND_END_ARG_INFO()
+
+static ZEND_BEGIN_ARG_INFO_EX( number_parse_currency_arginfo, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, string )
+ ZEND_ARG_INFO( 1, currency )
+ ZEND_ARG_INFO( 1, position )
+ZEND_END_ARG_INFO()
+
+static function_entry NumberFormatter_class_functions[] = {
+ PHP_ME( NumberFormatter, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( numfmt_create ), NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( format, ZEND_FN( numfmt_format ), NULL )
+ PHP_NAMED_FE( parse, ZEND_FN( numfmt_parse ), number_parse_arginfo )
+ PHP_NAMED_FE( formatCurrency, ZEND_FN( numfmt_format_currency ), NULL )
+ PHP_NAMED_FE( parseCurrency, ZEND_FN( numfmt_parse_currency ), number_parse_currency_arginfo )
+ PHP_NAMED_FE( setAttribute, ZEND_FN( numfmt_set_attribute ), NULL )
+ PHP_NAMED_FE( getAttribute, ZEND_FN( numfmt_get_attribute ), NULL )
+ PHP_NAMED_FE( setTextAttribute, ZEND_FN( numfmt_set_text_attribute ), NULL )
+ PHP_NAMED_FE( getTextAttribute, ZEND_FN( numfmt_get_text_attribute ), NULL )
+ PHP_NAMED_FE( setSymbol, ZEND_FN( numfmt_set_symbol ), NULL )
+ PHP_NAMED_FE( getSymbol, ZEND_FN( numfmt_get_symbol ), NULL )
+ PHP_NAMED_FE( setPattern, ZEND_FN( numfmt_set_pattern ), NULL )
+ PHP_NAMED_FE( getPattern, ZEND_FN( numfmt_get_pattern ), NULL )
+ PHP_NAMED_FE( getLocale, ZEND_FN( numfmt_get_locale ), NULL )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( numfmt_get_error_code ), NULL )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( numfmt_get_error_message ), NULL )
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ formatter_register_class
+ * Initialize 'NumberFormatter' class
+ */
+void formatter_register_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ // Create and register 'NumberFormatter' class.
+ INIT_CLASS_ENTRY( ce, "NumberFormatter", NumberFormatter_class_functions );
+ ce.create_object = NumberFormatter_object_create;
+ NumberFormatter_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ // Declare 'NumberFormatter' class properties.
+ if( !NumberFormatter_ce_ptr )
+ {
+ zend_error(E_ERROR, "Failed to register NumberFormatter class");
+ return;
+ }
+}
+/* }}} */
+
+/*
+ * 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/formatter/formatter_class.h b/ext/intl/formatter/formatter_class.h
new file mode 100755
index 0000000000..cf1cb060c6
--- /dev/null
+++ b/ext/intl/formatter/formatter_class.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_CLASS_H
+#define FORMATTER_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+#include "intl_data.h"
+#include "formatter_data.h"
+
+typedef struct {
+ zend_object zo;
+ formatter_data nf_data;
+} NumberFormatter_object;
+
+void formatter_register_class( TSRMLS_D );
+extern zend_class_entry *NumberFormatter_ce_ptr;
+
+/* Auxiliary macros */
+
+#define FORMATTER_METHOD_INIT_VARS INTL_METHOD_INIT_VARS(NumberFormatter, nfo)
+#define FORMATTER_METHOD_FETCH_OBJECT INTL_METHOD_FETCH_OBJECT(NumberFormatter, nfo)
+#define FORMATTER_OBJECT(nfo) (nfo)->nf_data.unum
+
+#endif // #ifndef FORMATTER_CLASS_H
diff --git a/ext/intl/formatter/formatter_data.c b/ext/intl/formatter/formatter_data.c
new file mode 100755
index 0000000000..88f122f7c9
--- /dev/null
+++ b/ext/intl/formatter/formatter_data.c
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "formatter_data.h"
+
+/* {{{ void formatter_data_init( formatter_data* nf_data )
+ * Initialize internals of formatter_data.
+ */
+void formatter_data_init( formatter_data* nf_data TSRMLS_DC )
+{
+ if( !nf_data )
+ return;
+
+ nf_data->unum = NULL;
+ intl_error_reset( &nf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void formatter_data_free( formatter_data* nf_data )
+ * Clean up mem allocted by internals of formatter_data
+ */
+void formatter_data_free( formatter_data* nf_data TSRMLS_DC )
+{
+ if( !nf_data )
+ return;
+
+ if( nf_data->unum )
+ unum_close( nf_data->unum );
+
+ nf_data->unum = NULL;
+ intl_error_reset( &nf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ formatter_data* formatter_data_create()
+ * Alloc mem for formatter_data and initialize it with default values.
+ */
+formatter_data* formatter_data_create( TSRMLS_D )
+{
+ formatter_data* nf_data = ecalloc( 1, sizeof(formatter_data) );
+
+ formatter_data_init( nf_data TSRMLS_CC );
+
+ return nf_data;
+}
+/* }}} */
+
+/*
+ * 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/formatter/formatter_data.h b/ext/intl/formatter/formatter_data.h
new file mode 100755
index 0000000000..adc4818afc
--- /dev/null
+++ b/ext/intl/formatter/formatter_data.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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_DATA_H
+#define FORMATTER_DATA_H
+
+#include <php.h>
+
+#include <unicode/unum.h>
+
+#include "intl_error.h"
+
+typedef struct {
+ // error hangling
+ intl_error error;
+
+ // formatter handling
+ UNumberFormat* unum;
+} formatter_data;
+
+formatter_data* formatter_data_create( TSRMLS_D );
+void formatter_data_init( formatter_data* nf_data TSRMLS_DC );
+void formatter_data_free( formatter_data* nf_data TSRMLS_DC );
+
+#endif // FORMATTER_DATA_H
diff --git a/ext/intl/formatter/formatter_format.c b/ext/intl/formatter/formatter_format.c
new file mode 100755
index 0000000000..5a8b7c90ae
--- /dev/null
+++ b/ext/intl/formatter/formatter_format.c
@@ -0,0 +1,207 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+#include "intl_convert.h"
+
+/* {{{ proto mixed NumberFormatter::format( mixed $num[, int $type] )
+ * Format a number. }}} */
+/* {{{ proto mixed numfmt_format( NumberFormatter $nf, mixed $num[, int type] )
+ * Format a number.
+ */
+PHP_FUNCTION( numfmt_format )
+{
+ zval **number;
+ long type = FORMAT_TYPE_DEFAULT;
+ UChar format_buf[32];
+ UChar* formatted = format_buf;
+ int formatted_len = USIZE(format_buf);
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OZ|l",
+ &object, NumberFormatter_ce_ptr, &number, &type ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_format: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ if(type == FORMAT_TYPE_DEFAULT) {
+ if(Z_TYPE_PP(number) == IS_STRING) {
+ SEPARATE_ZVAL_IF_NOT_REF(number);
+ if ((Z_TYPE_PP(number)=is_numeric_string(Z_STRVAL_PP(number), Z_STRLEN_PP(number),
+ &Z_LVAL_PP(number), &Z_DVAL_PP(number), 1)) == 0) {
+ ZVAL_LONG(*number, 0);
+ }
+ }
+
+ if(Z_TYPE_PP(number) == IS_LONG) {
+ // take INT32 on 32-bit, int64 on 64-bit
+ type = (sizeof(long) == 8)?FORMAT_TYPE_INT64:FORMAT_TYPE_INT32;
+ } else if(Z_TYPE_PP(number) == IS_DOUBLE) {
+ type = FORMAT_TYPE_DOUBLE;
+ } else {
+ type = FORMAT_TYPE_INT32;
+ }
+ }
+
+ if(Z_TYPE_PP(number) != IS_DOUBLE && Z_TYPE_PP(number) != IS_LONG) {
+ SEPARATE_ZVAL_IF_NOT_REF(number);
+ convert_scalar_to_number( *number TSRMLS_CC );
+ }
+
+ switch(type) {
+ case FORMAT_TYPE_INT32:
+ convert_to_long_ex(number);
+ formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_PP(number),
+ formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_PP(number),
+ formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ break;
+
+ case FORMAT_TYPE_INT64:
+ {
+ int64_t value = (Z_TYPE_PP(number) == IS_DOUBLE)?(int64_t)Z_DVAL_PP(number):Z_LVAL_PP(number);
+ formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ }
+ break;
+
+ case FORMAT_TYPE_DOUBLE:
+ convert_to_double_ex(number);
+ formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_PP(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_PP(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ break;
+
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported format type %ld", type);
+ RETURN_FALSE;
+ break;
+ }
+
+ INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+}
+/* }}} */
+
+/* {{{ proto mixed NumberFormatter::formatCurrency( double $num, string $currency )
+ * Format a number as currency. }}} */
+/* {{{ proto mixed numfmt_format_currency( NumberFormatter $nf, double $num, string $currency )
+ * Format a number as currency.
+ */
+PHP_FUNCTION( numfmt_format_currency )
+{
+ double number;
+ UChar format_buf[32];
+ UChar* formatted = format_buf;
+ int formatted_len = USIZE(format_buf);
+ char* currency = NULL;
+ int currency_len = 0;
+ UChar* scurrency = NULL;
+ int scurrency_len = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ods",
+ &object, NumberFormatter_ce_ptr, &number, &currency, &currency_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_format_currency: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert currency to UTF-16.
+ intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-16 failed" );
+
+ // Format the number using a fixed-length buffer.
+ formatted_len = unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+ // If the buffer turned out to be too small
+ // then allocate another buffer dynamically
+ // and use it to format the number.
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) {
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((nfo)) TSRMLS_CC );
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P(nfo), "Number formatting failed", 0 TSRMLS_CC );
+ RETVAL_FALSE;
+ if (formatted != format_buf) {
+ efree(formatted);
+ }
+ } else {
+ INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+ }
+
+ if(scurrency) {
+ efree(scurrency);
+ }
+}
+
+/* }}} */
+
+/*
+ * 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/formatter/formatter_format.h b/ext/intl/formatter/formatter_format.h
new file mode 100755
index 0000000000..35fafd1ede
--- /dev/null
+++ b/ext/intl/formatter/formatter_format.h
@@ -0,0 +1,31 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_FORMAT_H
+#define FORMATTER_FORMAT_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_format );
+PHP_FUNCTION( numfmt_format_currency );
+
+#define FORMAT_TYPE_DEFAULT 0
+#define FORMAT_TYPE_INT32 1
+#define FORMAT_TYPE_INT64 2
+#define FORMAT_TYPE_DOUBLE 3
+#define FORMAT_TYPE_CURRENCY 4
+
+#endif // FORMATTER_FORMAT_H
diff --git a/ext/intl/formatter/formatter_main.c b/ext/intl/formatter/formatter_main.c
new file mode 100755
index 0000000000..e868d8f2e0
--- /dev/null
+++ b/ext/intl/formatter/formatter_main.c
@@ -0,0 +1,220 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "intl_convert.h"
+
+/* {{{ proto NumberFormatter NumberFormatter::create( string $locale, int style[, string $pattern ] )
+ * Create formatter. }}} */
+/* {{{ proto NumberFormatter numfmt_create( string $locale, int style[, string $pattern ] )
+ * Create formatter.
+ */
+PHP_FUNCTION( numfmt_create )
+{
+ char* locale;
+ char* pattern = NULL;
+ int locale_len = 0, pattern_len = 0;
+ long style;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|s",
+ &locale, &locale_len, &style, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_create: unable to parse input parameters", 0 TSRMLS_CC );
+
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN(locale_len);
+ // Create a NumberFormatter object and save the ICU formatter into it.
+ if( ( object = getThis() ) == NULL )
+ object = return_value;
+
+ if( Z_TYPE_P( object ) != IS_OBJECT )
+ object_init_ex( object, NumberFormatter_ce_ptr );
+
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert pattern (if specified) to UTF-16.
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_create: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ // Create an ICU number formatter.
+ FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+ if(spattern) {
+ efree(spattern);
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( nfo ),
+ "numfmt_create: number formatter creation failed", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto void NumberFormatter::__construct( string $locale, int style[, string $pattern ] )
+ * NumberFormatter object constructor.
+ */
+PHP_METHOD( NumberFormatter, __construct )
+{
+ char* locale;
+ char* pattern = NULL;
+ int locale_len = 0, pattern_len = 0;
+ long style;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ object = getThis();
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|s",
+ &locale, &locale_len, &style, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "__construct: unable to parse input params", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, object);
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert pattern (if specified) to UTF-16.
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( nfo ),
+ "__construct: Error converting pattern to UTF-16", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ // Create an ICU number formatter.
+ FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+ if(spattern) {
+ efree(spattern);
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( nfo ),
+ "__construct: number formatter creation failed", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto int NumberFormatter::getErrorCode()
+ * Get formatter's last error code. }}} */
+/* {{{ proto int numfmt_get_error_code( NumberFormatter $nf )
+ * Get formatter's last error code.
+ */
+PHP_FUNCTION( numfmt_get_error_code )
+{
+ FORMATTER_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ nfo = (NumberFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ // Return formatter's last error code.
+ RETURN_LONG( INTL_DATA_ERROR_CODE(nfo) );
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getErrorMessage( )
+ * Get text description for formatter's last error code. }}} */
+/* {{{ proto string numfmt_get_error_message( NumberFormatter $nf )
+ * Get text description for formatter's last error code.
+ */
+PHP_FUNCTION( numfmt_get_error_message )
+{
+ char* message = NULL;
+ FORMATTER_METHOD_INIT_VARS
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ nfo = (NumberFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ // Return last error message.
+ message = intl_error_get_message( &INTL_DATA_ERROR(nfo) 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/formatter/formatter_main.h b/ext/intl/formatter/formatter_main.h
new file mode 100755
index 0000000000..7669e684ae
--- /dev/null
+++ b/ext/intl/formatter/formatter_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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_MAIN_H
+#define FORMATTER_MAIN_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_create );
+PHP_FUNCTION( numfmt_get_error_code );
+PHP_FUNCTION( numfmt_get_error_message );
+PHP_METHOD( NumberFormatter, __construct );
+
+#endif // FORMATTER_FORMAT_H
diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c
new file mode 100755
index 0000000000..47333acc49
--- /dev/null
+++ b/ext/intl/formatter/formatter_parse.c
@@ -0,0 +1,173 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+#include "formatter_parse.h"
+#include "intl_convert.h"
+
+/* {{{ proto mixed NumberFormatter::parse( string $str[, int $type, int &$position ])
+ * Parse a number. }}} */
+/* {{{ proto mixed numfmt_parse( NumberFormatter $nf, string $str[, int $type, int &$position ])
+ * Parse a number.
+ */
+PHP_FUNCTION( numfmt_parse )
+{
+ long type = FORMAT_TYPE_DOUBLE;
+ UChar* sstr = NULL;
+ int sstr_len = 0;
+ char* str = NULL;
+ int str_len;
+ int32_t val32, position = 0;
+ int64_t val64;
+ double val_double;
+ int32_t* position_p = NULL;
+ zval *zposition = NULL;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lz!",
+ &object, NumberFormatter_ce_ptr, &str, &str_len, &type, &zposition ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "number_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert given string to UTF-16.
+ intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
+
+ if(zposition) {
+ convert_to_long(zposition);
+ position = (int32_t)Z_LVAL_P( zposition );
+ position_p = &position;
+ }
+
+ switch(type) {
+ case FORMAT_TYPE_INT32:
+ val32 = unum_parse(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ RETVAL_LONG(val32);
+ break;
+ case FORMAT_TYPE_INT64:
+ val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ if(val64 > LONG_MAX || val64 < -LONG_MAX) {
+ RETVAL_DOUBLE(val64);
+ } else {
+ val32 = (int32_t)val64;
+ RETVAL_LONG(val32);
+ }
+ break;
+ case FORMAT_TYPE_DOUBLE:
+ val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ RETVAL_DOUBLE(val_double);
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported format type %ld", type);
+ RETVAL_FALSE;
+ break;
+ }
+ if(zposition) {
+ zval_dtor(zposition);
+ ZVAL_LONG(zposition, position);
+ }
+
+ efree(sstr);
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+}
+/* }}} */
+
+/* {{{ proto double NumberFormatter::parseCurrency( string $str, string $&currency[, int $&position] )
+ * Parse a number as currency. }}} */
+/* {{{ proto double numfmt_parse_currency( NumberFormatter $nf, string $str, string $&currency[, int $&position] )
+ * Parse a number as currency.
+ */
+PHP_FUNCTION( numfmt_parse_currency )
+{
+ double number;
+ UChar currency[5] = {0};
+ UChar* sstr = NULL;
+ int sstr_len = 0;
+ char *currency_str = NULL;
+ int currency_len = 0;
+ char *str;
+ int str_len;
+ int32_t* position_p = NULL;
+ int32_t position = 0;
+ zval *zcurrency, *zposition = NULL;
+ FORMATTER_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z!",
+ &object, NumberFormatter_ce_ptr, &str, &str_len, &zcurrency, &zposition ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "number_parse_currency: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ // Convert given string to UTF-16.
+ intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
+
+ if(zposition) {
+ convert_to_long(zposition);
+ position = (int32_t)Z_LVAL_P( zposition );
+ position_p = &position;
+ }
+
+ number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
+ if(zposition) {
+ zval_dtor(zposition);
+ ZVAL_LONG(zposition, position);
+ }
+ efree(sstr);
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+
+ // Convert parsed currency to UTF-8 and pass it back to caller.
+ intl_convert_utf16_to_utf8(&currency_str, &currency_len, currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" );
+ zval_dtor( zcurrency );
+ ZVAL_STRINGL(zcurrency, currency_str, currency_len, 0);
+
+ RETVAL_DOUBLE( number );
+}
+/* }}} */
+
+/*
+ * 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/formatter/formatter_parse.h b/ext/intl/formatter/formatter_parse.h
new file mode 100755
index 0000000000..cb96c72b5d
--- /dev/null
+++ b/ext/intl/formatter/formatter_parse.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_PARSE_H
+#define FORMATTER_PARSE_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_parse );
+PHP_FUNCTION( numfmt_parse_currency );
+
+#endif // FORMATTER_PARSE_H
diff --git a/ext/intl/grapheme/grapheme.h b/ext/intl/grapheme/grapheme.h
new file mode 100755
index 0000000000..c0e697ac1e
--- /dev/null
+++ b/ext/intl/grapheme/grapheme.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef GRAPHEME_GRAPHEME_H
+#define GRAPHEME_GRAPHEME_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+#include <unicode/ubrk.h>
+
+PHP_FUNCTION(grapheme_strlen);
+PHP_FUNCTION(grapheme_strpos);
+PHP_FUNCTION(grapheme_stripos);
+PHP_FUNCTION(grapheme_strrpos);
+PHP_FUNCTION(grapheme_strripos);
+PHP_FUNCTION(grapheme_substr);
+PHP_FUNCTION(grapheme_strstr);
+PHP_FUNCTION(grapheme_stristr);
+PHP_FUNCTION(grapheme_extract);
+
+void grapheme_register_constants( INIT_FUNC_ARGS );
+void grapheme_close_global_iterator( TSRMLS_D );
+
+#endif // GRAPHEME_GRAPHEME_H
diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c
new file mode 100755
index 0000000000..9b4ba82a25
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_string.c
@@ -0,0 +1,913 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Author: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* {{{ includes */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+#include "grapheme.h"
+#include "grapheme_util.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+#include <unicode/ubrk.h>
+
+#include "ext/standard/php_string.h"
+
+/* }}} */
+
+#define GRAPHEME_EXTRACT_TYPE_COUNT 0
+#define GRAPHEME_EXTRACT_TYPE_MAXBYTES 1
+#define GRAPHEME_EXTRACT_TYPE_MAXCHARS 2
+#define GRAPHEME_EXTRACT_TYPE_MIN GRAPHEME_EXTRACT_TYPE_COUNT
+#define GRAPHEME_EXTRACT_TYPE_MAX GRAPHEME_EXTRACT_TYPE_MAXCHARS
+
+
+/* {{{ grapheme_register_constants
+ * Register API constants
+ */
+void grapheme_register_constants( INIT_FUNC_ARGS )
+{
+ REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_COUNT", GRAPHEME_EXTRACT_TYPE_COUNT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_MAXBYTES", GRAPHEME_EXTRACT_TYPE_MAXBYTES, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("GRAPHEME_EXTR_MAXCHARS", GRAPHEME_EXTRACT_TYPE_MAXCHARS, CONST_CS | CONST_PERSISTENT);
+}
+/* }}} */
+
+/* {{{ proto int grapheme_strlen(string str)
+ Get number of graphemes in a string */
+PHP_FUNCTION(grapheme_strlen)
+{
+ unsigned char* string;
+ int string_len;
+ UChar* ustring = NULL;
+ int ustring_len = 0;
+ int ret_len;
+ UErrorCode status;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", (char **)&string, &string_len) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_strlen: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ ret_len = grapheme_ascii_check(string, string_len);
+
+ if ( ret_len >= 0 )
+ RETURN_LONG(ret_len);
+
+ /* convert the string to UTF-16. */
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&ustring, &ustring_len, (char*) string, string_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( ustring );
+ RETURN_NULL();
+ }
+
+ ret_len = grapheme_split_string(ustring, ustring_len, NULL, 0 TSRMLS_CC );
+
+ efree( ustring );
+
+ if (ret_len >= 0) {
+ RETVAL_LONG(ret_len);
+ } else {
+ RETVAL_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int grapheme_strpos(string haystack, string needle [, int offset ])
+ Find position of first occurrence of a string within another */
+PHP_FUNCTION(grapheme_strpos)
+{
+ unsigned char *haystack, *needle;
+ int haystack_len, needle_len;
+ unsigned char *found;
+ long loffset = 0;
+ int32_t offset = 0;
+ int ret_pos, uchar_pos;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_strpos: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( OUTSIDE_STRING(loffset, haystack_len) ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ offset = (int32_t) loffset;
+
+ /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
+
+ if (needle_len == 0) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+
+ /* quick check to see if the string might be there
+ * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
+ */
+ found = (unsigned char *)php_memnstr((char *)haystack + offset, (char *)needle, needle_len, (char *)haystack + haystack_len);
+
+ /* if it isn't there the we are done */
+ if (!found) {
+ RETURN_FALSE;
+ }
+
+ /* if it is there, and if the haystack is ascii, we are all done */
+ if ( grapheme_ascii_check(haystack, haystack_len) >= 0 ) {
+
+ RETURN_LONG(found - haystack);
+ }
+
+ /* do utf16 part of the strpos */
+ ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, &uchar_pos, 0 /* fIgnoreCase */ TSRMLS_CC );
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos + offset);
+ } else {
+ RETURN_FALSE;
+ }
+
+}
+/* }}} */
+
+/* {{{ proto int grapheme_stripos(string haystack, string needle [, int offset ])
+ Find position of first occurrence of a string within another, ignoring case differences */
+PHP_FUNCTION(grapheme_stripos)
+{
+ unsigned char *haystack, *needle, *haystack_dup, *needle_dup;
+ int haystack_len, needle_len;
+ unsigned char *found;
+ long loffset = 0;
+ int32_t offset = 0;
+ int ret_pos, uchar_pos;
+ int is_ascii;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_stripos: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( OUTSIDE_STRING(loffset, haystack_len) ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_stripos: Offset not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ offset = (int32_t) loffset;
+
+ /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
+
+ if (needle_len == 0) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_stripos: Empty delimiter", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+
+ is_ascii = ( grapheme_ascii_check(haystack, haystack_len) >= 0 );
+
+ if ( is_ascii ) {
+ needle_dup = (unsigned char *)estrndup((char *)needle, needle_len);
+ php_strtolower((char *)needle_dup, needle_len);
+ haystack_dup = (unsigned char *)estrndup((char *)haystack, haystack_len);
+ php_strtolower((char *)haystack_dup, haystack_len);
+
+ found = (unsigned char*) php_memnstr((char *)haystack_dup + offset, (char *)needle_dup, needle_len, (char *)haystack_dup + haystack_len);
+
+ efree(haystack_dup);
+ efree(needle_dup);
+
+ if (found) {
+ RETURN_LONG(found - haystack_dup);
+ }
+
+ /* if needle was ascii too, we are all done, otherwise we need to try using Unicode to see what we get */
+ if ( grapheme_ascii_check(needle, needle_len) >= 0 ) {
+ RETURN_FALSE;
+ }
+ }
+
+ /* do utf16 part of the strpos */
+ ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, offset, &uchar_pos, 1 /* fIgnoreCase */ TSRMLS_CC );
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos + offset);
+ } else {
+ RETURN_FALSE;
+ }
+
+}
+/* }}} */
+
+/* {{{ proto int grapheme_strrpos(string haystack, string needle [, int offset])
+ Find position of last occurrence of a string within another */
+PHP_FUNCTION(grapheme_strrpos)
+{
+ unsigned char *haystack, *needle;
+ int haystack_len, needle_len;
+ long loffset = 0;
+ int32_t offset = 0;
+ int32_t ret_pos;
+ int is_ascii;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_strrpos: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( OUTSIDE_STRING(loffset, haystack_len) ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ offset = (int32_t) loffset;
+
+ /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
+
+ if (needle_len == 0) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ is_ascii = grapheme_ascii_check(haystack, haystack_len) >= 0;
+
+ if ( is_ascii ) {
+
+ ret_pos = grapheme_strrpos_ascii(haystack, haystack_len, needle, needle_len, offset);
+
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos);
+ }
+
+ /* if the needle was ascii too, we are done */
+
+ if ( grapheme_ascii_check(needle, needle_len) >= 0 ) {
+ RETURN_FALSE;
+ }
+
+ /* else we need to continue via utf16 */
+ }
+
+ ret_pos = grapheme_strrpos_utf16(haystack, haystack_len, needle, needle_len, offset, 0 /* f_ignore_case */ TSRMLS_CC);
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos);
+ } else {
+ RETURN_FALSE;
+ }
+
+
+}
+/* }}} */
+
+/* {{{ proto int grapheme_strripos(string haystack, string needle [, int offset])
+ Find position of last occurrence of a string within another, ignoring case */
+PHP_FUNCTION(grapheme_strripos)
+{
+ unsigned char *haystack, *needle;
+ int haystack_len, needle_len;
+ long loffset = 0;
+ int32_t offset = 0;
+ int32_t ret_pos;
+ int is_ascii;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|l", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &loffset) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_strrpos: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( OUTSIDE_STRING(loffset, haystack_len) ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ offset = (int32_t) loffset;
+
+ /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
+
+ if (needle_len == 0) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ is_ascii = grapheme_ascii_check(haystack, haystack_len) >= 0;
+
+ if ( is_ascii ) {
+ unsigned char *needle_dup, *haystack_dup;
+
+ needle_dup = (unsigned char *)estrndup((char *)needle, needle_len);
+ php_strtolower((char *)needle_dup, needle_len);
+ haystack_dup = (unsigned char *)estrndup((char *)haystack, haystack_len);
+ php_strtolower((char *)haystack_dup, haystack_len);
+
+ ret_pos = grapheme_strrpos_ascii(haystack_dup, haystack_len, needle_dup, needle_len, offset);
+
+ efree(haystack_dup);
+ efree(needle_dup);
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos);
+ }
+
+ /* if the needle was ascii too, we are done */
+
+ if ( grapheme_ascii_check(needle, needle_len) >= 0 ) {
+ RETURN_FALSE;
+ }
+
+ /* else we need to continue via utf16 */
+ }
+
+ ret_pos = grapheme_strrpos_utf16(haystack, haystack_len, needle, needle_len, offset, 1 /* f_ignore_case */ TSRMLS_CC);
+
+ if ( ret_pos >= 0 ) {
+ RETURN_LONG(ret_pos);
+ } else {
+ RETURN_FALSE;
+ }
+
+
+}
+/* }}} */
+
+/* {{{ proto string grapheme_substr(string str, int start [, int length])
+ Returns part of a string */
+PHP_FUNCTION(grapheme_substr)
+{
+ unsigned char *str, *sub_str;
+ UChar *ustr;
+ int str_len, sub_str_len, ustr_len;
+ long lstart = 0, length = 0;
+ int32_t start = 0;
+ int iter_val;
+ UErrorCode status;
+ unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
+ UBreakIterator* bi = NULL;
+ int sub_str_start_pos, sub_str_end_pos;
+ int32_t (*iter_func)(UBreakIterator *);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", (char **)&str, &str_len, &lstart, &length) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_substr: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( OUTSIDE_STRING(lstart, str_len) ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ start = (int32_t) lstart;
+
+ /* the offset is 'grapheme count offset' so it still might be invalid - we'll check it later */
+
+ if ( grapheme_ascii_check(str, str_len) >= 0 ) {
+ grapheme_substr_ascii((char *)str, str_len, start, length, ZEND_NUM_ARGS(), (char **) &sub_str, &sub_str_len);
+
+ if ( NULL == sub_str ) {
+ RETURN_FALSE;
+ }
+
+ RETURN_STRINGL(((char *)sub_str), sub_str_len, 1);
+ }
+
+ ustr = NULL;
+ ustr_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)str, str_len, &status);
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( ustr );
+ RETURN_FALSE;
+ }
+
+ bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &status TSRMLS_CC );
+
+ if( U_FAILURE(status) ) {
+ RETURN_FALSE;
+ }
+
+ ubrk_setText(bi, ustr, ustr_len, &status);
+
+ if ( start < 0 ) {
+ iter_func = ubrk_previous;
+ ubrk_last(bi);
+ iter_val = 1;
+ }
+ else {
+ iter_func = ubrk_next;
+ iter_val = -1;
+ }
+
+ sub_str_start_pos = 0;
+
+ while ( start ) {
+ sub_str_start_pos = iter_func(bi);
+
+ if ( UBRK_DONE == sub_str_start_pos ) {
+ break;
+ }
+
+ start += iter_val;
+ }
+
+ if ( 0 != start || sub_str_start_pos >= ustr_len ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC );
+
+ efree(ustr);
+ ubrk_close(bi);
+ RETURN_FALSE;
+ }
+
+ if (ZEND_NUM_ARGS() <= 2) {
+
+ /* no length supplied, return the rest of the string */
+
+ sub_str = NULL;
+ sub_str_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ustr_len - sub_str_start_pos, &status);
+
+ efree( ustr );
+ ubrk_close( bi );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 1 TSRMLS_CC );
+
+ efree( sub_str );
+
+ RETURN_FALSE;
+ }
+
+ /* return the allocated string, not a duplicate */
+ RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
+ }
+
+ /* find the end point of the string to return */
+
+ if ( length < 0 ) {
+ iter_func = ubrk_previous;
+ ubrk_last(bi);
+ iter_val = 1;
+ }
+ else {
+ iter_func = ubrk_next;
+ iter_val = -1;
+ }
+
+ sub_str_end_pos = 0;
+
+ while ( length ) {
+ sub_str_end_pos = iter_func(bi);
+
+ if ( UBRK_DONE == sub_str_end_pos ) {
+ break;
+ }
+
+ length += iter_val;
+ }
+
+ if ( UBRK_DONE == sub_str_end_pos ) {
+
+ 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;
+ }
+
+ sub_str = NULL;
+ status = U_ZERO_ERROR;
+ intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ( sub_str_end_pos - sub_str_start_pos ), &status);
+
+ efree( ustr );
+ ubrk_close( bi );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 1 TSRMLS_CC );
+
+ if ( NULL != sub_str )
+ efree( sub_str );
+
+ RETURN_FALSE;
+ }
+
+ /* return the allocated string, not a duplicate */
+ RETURN_STRINGL(((char *)sub_str), sub_str_len, 0);
+
+}
+/* }}} */
+
+/* {{{ strstr_common_handler */
+static void strstr_common_handler(INTERNAL_FUNCTION_PARAMETERS, int f_ignore_case)
+{
+ unsigned char *haystack, *needle, *found;
+ int haystack_len, needle_len;
+ int ret_pos, uchar_pos;
+ zend_bool part = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", (char **)&haystack, &haystack_len, (char **)&needle, &needle_len, &part) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_strstr: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if (needle_len == 0) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Empty delimiter", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+
+ if ( !f_ignore_case ) {
+
+ /* ASCII optimization: quick check to see if the string might be there
+ * I realize that 'offset' is 'grapheme count offset' but will work in spite of that
+ */
+ found = (unsigned char *)php_memnstr((char *)haystack, (char *)needle, needle_len, (char *)haystack + haystack_len);
+
+ /* if it isn't there the we are done */
+ if ( !found ) {
+ RETURN_FALSE;
+ }
+
+ /* if it is there, and if the haystack is ascii, we are all done */
+ if ( grapheme_ascii_check(haystack, haystack_len) >= 0 ) {
+ size_t found_offset = found - haystack;
+
+ if (part) {
+ RETURN_STRINGL(((char *)haystack) , found_offset, 1);
+ } else {
+ RETURN_STRINGL(((char *)found), haystack_len - found_offset, 1);
+ }
+ }
+
+ }
+
+ /* need to work in utf16 */
+ ret_pos = grapheme_strpos_utf16(haystack, haystack_len, needle, needle_len, 0, &uchar_pos, f_ignore_case TSRMLS_CC );
+
+ if ( ret_pos < 0 ) {
+ RETURN_FALSE;
+ }
+
+ /* uchar_pos is the 'nth' Unicode character position of the needle */
+
+ ret_pos = 0;
+ U8_FWD_N(haystack, ret_pos, haystack_len, uchar_pos);
+
+ if (part) {
+ RETURN_STRINGL(((char *)haystack), ret_pos, 1);
+ }
+ else {
+ RETURN_STRINGL(((char *)haystack) + ret_pos, haystack_len - ret_pos, 1);
+ }
+
+}
+/* }}} */
+
+/* {{{ proto string grapheme_strstr(string haystack, string needle[, bool part])
+ Finds first occurrence of a string within another */
+PHP_FUNCTION(grapheme_strstr)
+{
+ strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0 /* f_ignore_case */);
+}
+/* }}} */
+
+/* {{{ proto string grapheme_stristr(string haystack, string needle[, bool part])
+ Finds first occurrence of a string within another */
+PHP_FUNCTION(grapheme_stristr)
+{
+ strstr_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1 /* f_ignore_case */);
+}
+/* }}} */
+
+/* {{{ grapheme_extract_charcount_iter - grapheme iterator for grapheme_extract MAXCHARS */
+inline int32_t
+grapheme_extract_charcount_iter(UBreakIterator *bi, int32_t csize, unsigned char *pstr, int32_t str_len)
+{
+ int pos = 0, prev_pos = 0;
+ int ret_pos = 0, prev_ret_pos = 0;
+
+ while ( 1 ) {
+ pos = ubrk_next(bi);
+
+ if ( UBRK_DONE == pos ) {
+ break;
+ }
+
+ /* if we are beyond our limit, then the loop is done */
+ if ( pos > csize ) {
+ break;
+ }
+
+ /* update our pointer in the original UTF-8 buffer by as many characters
+ as ubrk_next iterated over */
+
+ prev_ret_pos = ret_pos;
+ U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
+
+ if ( prev_ret_pos == ret_pos ) {
+ /* something wrong - malformed utf8? */
+ break;
+ }
+
+ prev_pos = pos;
+ }
+
+ return ret_pos;
+}
+/* }}} */
+
+/* {{{ grapheme_extract_bytecount_iter - grapheme iterator for grapheme_extract MAXBYTES */
+inline int32_t
+grapheme_extract_bytecount_iter(UBreakIterator *bi, int32_t bsize, unsigned char *pstr, int32_t str_len)
+{
+ int pos = 0, prev_pos = 0;
+ int ret_pos = 0, prev_ret_pos = 0;
+
+ while ( 1 ) {
+ pos = ubrk_next(bi);
+
+ if ( UBRK_DONE == pos ) {
+ break;
+ }
+
+ prev_ret_pos = ret_pos;
+ U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos);
+
+ if ( ret_pos > bsize ) {
+ ret_pos = prev_ret_pos;
+ break;
+ }
+
+ if ( prev_ret_pos == ret_pos ) {
+ /* something wrong - malformed utf8? */
+ break;
+ }
+
+ prev_pos = pos;
+ }
+
+ return ret_pos;
+}
+/* }}} */
+
+/* {{{ grapheme_extract_count_iter - grapheme iterator for grapheme_extract COUNT */
+inline int32_t
+grapheme_extract_count_iter(UBreakIterator *bi, int32_t size, unsigned char *pstr, int32_t str_len)
+{
+ int pos = 0, next_pos = 0;
+ int ret_pos = 0;
+
+ while ( size ) {
+ next_pos = ubrk_next(bi);
+
+ if ( UBRK_DONE == next_pos ) {
+ break;
+ }
+ pos = next_pos;
+ size--;
+ }
+
+ /* pos is one past the last UChar - and represent the number of code units to
+ advance in the utf-8 buffer
+ */
+
+ U8_FWD_N(pstr, ret_pos, str_len, pos);
+
+ return ret_pos;
+}
+/* }}} */
+
+/* {{{ grapheme extract iter function pointer array */
+typedef int32_t (*grapheme_extract_iter)(UBreakIterator * /*bi*/, int32_t /*size*/, unsigned char * /*pstr*/, int32_t /*str_len*/);
+
+static grapheme_extract_iter grapheme_extract_iters[] = {
+ &grapheme_extract_count_iter,
+ &grapheme_extract_bytecount_iter,
+ &grapheme_extract_charcount_iter,
+};
+/* }}} */
+
+/* {{{ proto string grapheme_extract(string str, int size[, int extract_type[, int start[, int next]]])
+ Function to extract a sequence of default grapheme clusters */
+PHP_FUNCTION(grapheme_extract)
+{
+ unsigned char *str, *pstr;
+ UChar *ustr;
+ int str_len, ustr_len;
+ long size; /* maximum number of grapheme clusters, bytes, or characters (based on extract_type) to return */
+ long lstart = 0; /* starting position in str in bytes */
+ int32_t start = 0;
+ long extract_type = GRAPHEME_EXTRACT_TYPE_COUNT;
+ UErrorCode status;
+ unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
+ UBreakIterator* bi = NULL;
+ int ret_pos;
+ zval *next = NULL; // return offset of next part of the string
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|llz", (char **)&str, &str_len, &size, &extract_type, &lstart, &next) == FAILURE) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_extract: unable to parse input param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( NULL != next ) {
+ if ( !PZVAL_IS_REF(next) ) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_extract: 'next' was not passed by reference", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+ else {
+ /* initialize next */
+ ZVAL_LONG(next, start);
+ }
+ }
+
+ if ( extract_type < GRAPHEME_EXTRACT_TYPE_MIN || extract_type > GRAPHEME_EXTRACT_TYPE_MAX ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_extract: unknown extract type param", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if ( lstart > INT32_MAX || lstart < 0 || lstart >= str_len ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: start not contained in string", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* we checked that it will fit: */
+ start = (int32_t) lstart;
+
+ pstr = str + start;
+
+ /* just in case pstr points in the middle of a character, move forward to the start of the next char */
+ if ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
+ unsigned char *str_end = str + str_len;
+
+ while ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
+ pstr++;
+ if ( pstr >= str_end ) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "grapheme_extract: invalid input string", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+ }
+ }
+
+ str_len -= (pstr - str);
+
+ /* if the string is all ASCII up to size+1 - or str_len whichever is first - then we are done.
+ (size + 1 because the size-th character might be the beginning of a grapheme cluster)
+ */
+
+ if ( -1 != grapheme_ascii_check(pstr, size + 1 < str_len ? size + 1 : str_len ) ) {
+ if ( NULL != next ) {
+ ZVAL_LONG(next, start+size);
+ }
+ RETURN_STRINGL(((char *)pstr), size, 1);
+ }
+
+ /* convert the strings to UTF-16. */
+ ustr = NULL;
+ ustr_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&ustr, &ustr_len, (char *)pstr, str_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+
+ if ( NULL != ustr )
+ efree( ustr );
+
+ RETURN_FALSE;
+ }
+
+ bi = NULL;
+ status = U_ZERO_ERROR;
+ bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
+
+ ubrk_setText(bi, ustr, ustr_len, &status);
+
+ /* if the caller put us in the middle of a grapheme, we can't detect it in all cases since we
+ can't back up. So, we will not do anything. */
+
+ /* now we need to find the end of the chunk the user wants us to return */
+
+ ret_pos = (*grapheme_extract_iters[extract_type])(bi, size, pstr, str_len);
+
+ efree(ustr);
+ ubrk_close(bi);
+
+ if ( NULL != next ) {
+ ZVAL_LONG(next, start+ret_pos);
+ }
+
+ RETURN_STRINGL(((char *)pstr), ret_pos, 1);
+}
+
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
+
diff --git a/ext/intl/grapheme/grapheme_util.c b/ext/intl/grapheme/grapheme_util.c
new file mode 100755
index 0000000000..375c695b7d
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_util.c
@@ -0,0 +1,619 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Author: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+/* {{{ includes */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+#include "grapheme.h"
+#include "grapheme_util.h"
+#include "intl_common.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+#include <unicode/ubrk.h>
+
+#include "ext/standard/php_string.h"
+
+ZEND_EXTERN_MODULE_GLOBALS( intl )
+
+/* }}} */
+
+/* {{{ grapheme_close_global_iterator - clean up */
+void
+grapheme_close_global_iterator( TSRMLS_D )
+{
+ UBreakIterator *global_break_iterator = INTL_G( grapheme_iterator );
+
+ if ( NULL != global_break_iterator ) {
+ ubrk_close(global_break_iterator);
+ }
+}
+/* }}} */
+
+/* {{{ grapheme_intl_case_fold: convert string to lowercase */
+void
+grapheme_intl_case_fold(UChar** ptr_to_free, UChar **str, int32_t *str_len, UErrorCode *pstatus )
+{
+ UChar *dest;
+ int32_t dest_len, size_required;
+
+ /* allocate a destination string that is a bit larger than the src, hoping that is enough */
+ dest_len = (*str_len) + ( *str_len / 10 );
+ dest = (UChar*) eumalloc(dest_len);
+
+ *pstatus = U_ZERO_ERROR;
+ size_required = u_strFoldCase(dest, dest_len, *str, *str_len, U_FOLD_CASE_DEFAULT, pstatus);
+
+ dest_len = size_required;
+
+ if ( U_BUFFER_OVERFLOW_ERROR == *pstatus ) {
+
+ dest = (UChar*) eurealloc(dest, dest_len);
+
+ *pstatus = U_ZERO_ERROR;
+ size_required = u_strFoldCase(dest, dest_len, *str, *str_len, U_FOLD_CASE_DEFAULT, pstatus);
+ }
+
+ if ( U_FAILURE(*pstatus) ) {
+ return;
+ }
+
+ if ( NULL != ptr_to_free) {
+ efree(*ptr_to_free);
+ *ptr_to_free = dest;
+ }
+
+ *str = dest;
+ *str_len = dest_len;
+
+ return;
+}
+/* }}} */
+
+/* {{{ grapheme_substr_ascii f='from' - starting point, l='length' */
+void
+grapheme_substr_ascii(char *str, int str_len, int f, int l, int argc, char **sub_str, int *sub_str_len)
+{
+ *sub_str = NULL;
+
+ if (argc > 2) {
+ if ((l < 0 && -l > str_len)) {
+ return;
+ } else if (l > str_len) {
+ l = str_len;
+ }
+ } else {
+ l = str_len;
+ }
+
+ if (f > str_len || (f < 0 && -f > str_len)) {
+ return;
+ }
+
+ if (l < 0 && (l + str_len - f) < 0) {
+ return;
+ }
+
+ /* if "from" position is negative, count start position from the end
+ * of the string
+ */
+ if (f < 0) {
+ f = str_len + f;
+ if (f < 0) {
+ f = 0;
+ }
+ }
+
+
+ /* if "length" position is negative, set it to the length
+ * needed to stop that many chars from the end of the string
+ */
+ if (l < 0) {
+ l = (str_len - f) + l;
+ if (l < 0) {
+ l = 0;
+ }
+ }
+
+ if (f >= str_len) {
+ return;
+ }
+
+ if ((f + l) > str_len) {
+ l = str_len - f;
+ }
+
+ *sub_str = str + f;
+ *sub_str_len = l;
+
+ return;
+}
+/* }}} */
+
+/* {{{ grapheme_strrpos_utf16 - strrpos using utf16 */
+int
+grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int f_ignore_case TSRMLS_DC)
+{
+ UChar *uhaystack, *puhaystack, *uhaystack_end, *uneedle;
+ int32_t uhaystack_len, uneedle_len;
+ UErrorCode status;
+ unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
+ UBreakIterator* bi = NULL;
+ int ret_pos, pos;
+
+ /* convert the strings to UTF-16. */
+ uhaystack = NULL;
+ uhaystack_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&uhaystack, &uhaystack_len, (char *) haystack, haystack_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( uhaystack );
+ return -1;
+ }
+
+ if ( f_ignore_case ) {
+ grapheme_intl_case_fold(&uhaystack, &uhaystack, &uhaystack_len, &status );
+ }
+
+ /* get a pointer to the haystack taking into account the offset */
+ bi = NULL;
+ status = U_ZERO_ERROR;
+ bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
+
+ puhaystack = grapheme_get_haystack_offset(bi, uhaystack, uhaystack_len, offset);
+
+ if ( NULL == puhaystack ) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
+ efree( uhaystack );
+ ubrk_close (bi);
+ return -1;
+ }
+
+ uneedle = NULL;
+ uneedle_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&uneedle, &uneedle_len, (char *) needle, needle_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( uhaystack );
+ efree( uneedle );
+ ubrk_close (bi);
+ return -1;
+ }
+
+ if ( f_ignore_case ) {
+ grapheme_intl_case_fold(&uneedle, &uneedle, &uneedle_len, &status );
+ }
+
+ ret_pos = -1; /* -1 represents 'not found' */
+
+ /* back up until there's needle_len characters to compare */
+
+ uhaystack_end = uhaystack + uhaystack_len;
+ pos = ubrk_last(bi);
+ puhaystack = uhaystack + pos;
+
+ while ( uhaystack_end - puhaystack < uneedle_len ) {
+
+ pos = ubrk_previous(bi);
+
+ if ( UBRK_DONE == pos ) {
+ break;
+ }
+
+ puhaystack = uhaystack + pos;
+ }
+
+ /* is there enough haystack left to hold the needle? */
+ if ( ( uhaystack_end - puhaystack ) < uneedle_len ) {
+ /* not enough, not found */
+ goto exit;
+ }
+
+ while ( UBRK_DONE != pos ) {
+
+ if (!u_memcmp(uneedle, puhaystack, uneedle_len)) { /* needle_len - 1 in zend memnstr? */
+
+ /* does the grapheme in the haystack end at the same place as the last grapheme in the needle? */
+
+ if ( ubrk_isBoundary(bi, pos + uneedle_len) ) {
+
+ /* found it, get grapheme count offset */
+ ret_pos = grapheme_count_graphemes(bi, uhaystack, pos);
+ break;
+ }
+
+ /* set position back */
+ ubrk_isBoundary(bi, pos);
+ }
+
+ pos = ubrk_previous(bi);
+ puhaystack = uhaystack + pos;
+ }
+
+exit:
+ efree( uhaystack );
+ efree( uneedle );
+ ubrk_close (bi);
+
+ return ret_pos;
+}
+
+/* }}} */
+
+/* {{{ grapheme_strpos_utf16 - strrpos using utf16*/
+int
+grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int32_t *puchar_pos, int f_ignore_case TSRMLS_DC)
+{
+ UChar *uhaystack, *puhaystack, *uneedle;
+ int32_t uhaystack_len, uneedle_len;
+ int ret_pos;
+ unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
+ UBreakIterator* bi;
+ UErrorCode status;
+
+ *puchar_pos = -1;
+
+ /* convert the strings to UTF-16. */
+
+ uhaystack = NULL;
+ uhaystack_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&uhaystack, &uhaystack_len, (char *) haystack, haystack_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( uhaystack );
+ return -1;
+ }
+
+ /* get a pointer to the haystack taking into account the offset */
+ bi = NULL;
+ status = U_ZERO_ERROR;
+ bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC );
+
+ puhaystack = grapheme_get_haystack_offset(bi, uhaystack, uhaystack_len, offset);
+ uhaystack_len = (uhaystack_len - ( puhaystack - uhaystack));
+
+ if ( NULL == puhaystack ) {
+
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC );
+
+ efree( uhaystack );
+ ubrk_close (bi);
+
+ return -1;
+ }
+
+ if ( f_ignore_case ) {
+ grapheme_intl_case_fold(&uhaystack, &puhaystack, &uhaystack_len, &status );
+ }
+
+ uneedle = NULL;
+ uneedle_len = 0;
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&uneedle, &uneedle_len, (char *) needle, needle_len, &status );
+
+ if ( U_FAILURE( status ) ) {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( uhaystack );
+ efree( uneedle );
+ ubrk_close (bi);
+
+ return -1;
+ }
+
+ if ( f_ignore_case ) {
+ grapheme_intl_case_fold(&uneedle, &uneedle, &uneedle_len, &status );
+ }
+
+ ret_pos = grapheme_memnstr_grapheme(bi, puhaystack, uneedle, uneedle_len, puhaystack + uhaystack_len );
+
+ *puchar_pos = ubrk_current(bi);
+
+ efree( uhaystack );
+ efree( uneedle );
+ ubrk_close (bi);
+
+ return ret_pos;
+}
+
+/* }}} */
+
+/* {{{ grapheme_ascii_check: ASCII check */
+int grapheme_ascii_check(const unsigned char *day, int32_t len)
+{
+ int ret_len = len;
+ while ( len-- ) {
+ if ( *day++ > 0x7f )
+ return -1;
+ }
+
+ return ret_len;
+}
+
+/* }}} */
+
+/* {{{ grapheme_split_string: find and optionally return grapheme boundaries */
+int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_array[], int boundary_array_len TSRMLS_DC )
+{
+ unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE];
+ UErrorCode status = U_ZERO_ERROR;
+ int ret_len, pos;
+ UBreakIterator* bi;
+
+ bi = grapheme_get_break_iterator((void*)u_break_iterator_buffer, &status TSRMLS_CC );
+
+ if( U_FAILURE(status) ) {
+ return -1;
+ }
+
+ ubrk_setText(bi, text, text_length, &status);
+
+ pos = 0;
+
+ for ( ret_len = 0; pos != UBRK_DONE; ) {
+
+ pos = ubrk_next(bi);
+
+ if ( pos != UBRK_DONE ) {
+
+ if ( NULL != boundary_array && ret_len < boundary_array_len ) {
+ boundary_array[ret_len] = pos;
+ }
+
+ ret_len++;
+ }
+ }
+
+ ubrk_close(bi);
+
+ return ret_len;
+}
+/* }}} */
+
+/* {{{ grapheme_count_graphemes */
+inline int32_t
+grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len)
+{
+ int ret_len = 0;
+ int pos = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ ubrk_setText(bi, string, string_len, &status);
+
+ do {
+
+ pos = ubrk_next(bi);
+
+ if ( UBRK_DONE != pos ) {
+ ret_len++;
+ }
+
+ } while ( UBRK_DONE != pos );
+
+ return ret_len;
+}
+/* }}} */
+
+/* {{{ grapheme_memnstr_grapheme: find needle in haystack using grapheme boundaries */
+inline int32_t
+grapheme_memnstr_grapheme(UBreakIterator *bi, UChar *haystack, UChar *needle, int32_t needle_len, UChar *end)
+{
+ UChar *p = haystack;
+ UChar ne = needle[needle_len-1];
+ UErrorCode status;
+ int32_t grapheme_offset;
+
+ end -= needle_len;
+
+ while (p <= end) {
+
+ if ((p = u_memchr(p, *needle, (end-p+1))) && ne == p[needle_len-1]) {
+
+ if (!u_memcmp(needle, p, needle_len - 1)) { /* needle_len - 1 works because if needle_len is 1, we've already tested the char */
+
+ /* does the grapheme end here? */
+
+ status = U_ZERO_ERROR;
+ ubrk_setText (bi, haystack, (end - haystack) + needle_len, &status);
+
+ if ( ubrk_isBoundary (bi, (p - haystack) + needle_len) ) {
+
+ /* found it, get grapheme count offset */
+ grapheme_offset = grapheme_count_graphemes(bi, haystack, (p - haystack));
+
+ return grapheme_offset;
+ }
+ }
+ }
+
+ if (p == NULL) {
+ return -1;
+ }
+
+ p++;
+ }
+
+ return -1;
+}
+
+/* }}} */
+
+/* {{{ grapheme_memrstr_grapheme: reverse find needle in haystack using grapheme boundaries */
+inline void *grapheme_memrchr_grapheme(const void *s, int c, int32_t n)
+{
+ register unsigned char *e;
+
+ if (n <= 0) {
+ return NULL;
+ }
+
+ for (e = (unsigned char *)s + n - 1; e >= (unsigned char *)s; e--) {
+ if (*e == (unsigned char)c) {
+ return (void *)e;
+ }
+ }
+
+ return NULL;
+}
+/* }}} */
+
+/* {{{ grapheme_get_haystack_offset - bump the haystack pointer based on the grapheme count offset */
+UChar *
+grapheme_get_haystack_offset(UBreakIterator* bi, UChar *uhaystack, int32_t uhaystack_len, int32_t offset)
+{
+ UErrorCode status;
+ int32_t pos;
+ int32_t (*iter_op)(UBreakIterator* bi);
+ int iter_incr;
+
+ if ( NULL != bi ) {
+ status = U_ZERO_ERROR;
+ ubrk_setText (bi, uhaystack, uhaystack_len, &status);
+ }
+
+ if ( 0 == offset ) {
+ return uhaystack;
+ }
+
+ if ( offset < 0 ) {
+ iter_op = ubrk_previous;
+ ubrk_last(bi); /* one past the end */
+ iter_incr = 1;
+ }
+ else {
+ iter_op = ubrk_next;
+ iter_incr = -1;
+ }
+
+ pos = 0;
+
+ while ( pos != UBRK_DONE && offset != 0 ) {
+
+ pos = iter_op(bi);
+
+ if ( UBRK_DONE != pos ) {
+ offset += iter_incr;
+ }
+ }
+
+ if ( offset != 0 ) {
+ return NULL;
+ }
+
+ return uhaystack + pos;
+}
+/* }}} */
+
+/* {{{ grapheme_strrpos_ascii: borrowed from the php ext/standard/string.c */
+ int32_t
+grapheme_strrpos_ascii(unsigned char *haystack, int32_t haystack_len, unsigned char *needle, int32_t needle_len, int32_t offset)
+{
+ unsigned char *p, *e;
+
+ if (offset >= 0) {
+ p = haystack + offset;
+ e = haystack + haystack_len - needle_len;
+ } else {
+ p = haystack;
+ if (needle_len > -offset) {
+ e = haystack + haystack_len - needle_len;
+ } else {
+ e = haystack + haystack_len + offset;
+ }
+ }
+
+ if (needle_len == 1) {
+ /* Single character search can shortcut memcmps */
+ while (e >= p) {
+ if (*e == *needle) {
+ return (e - p + (offset > 0 ? offset : 0));
+ }
+ e--;
+ }
+ return -1;
+ }
+
+ while (e >= p) {
+ if (memcmp(e, needle, needle_len) == 0) {
+ return (e - p + (offset > 0 ? offset : 0));
+ }
+ e--;
+ }
+
+ return -1;
+}
+
+/* }}} */
+
+/* {{{ grapheme_get_break_iterator: get a clone of the global character break iterator */
+UBreakIterator*
+grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status TSRMLS_DC )
+{
+ int32_t buffer_size;
+
+ UBreakIterator *global_break_iterator = INTL_G( grapheme_iterator );
+
+ if ( NULL == global_break_iterator ) {
+
+ global_break_iterator = ubrk_open(UBRK_CHARACTER,
+ NULL, /* icu default locale - locale has no effect on this iterator */
+ NULL, /* text not set in global iterator */
+ 0, /* text length = 0 */
+ status);
+
+ INTL_G(grapheme_iterator) = global_break_iterator;
+ }
+
+ buffer_size = U_BRK_SAFECLONE_BUFFERSIZE;
+
+ return ubrk_safeClone(global_break_iterator, stack_buffer, &buffer_size, status);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
+
diff --git a/ext/intl/grapheme/grapheme_util.h b/ext/intl/grapheme/grapheme_util.h
new file mode 100755
index 0000000000..f8207cac52
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_util.h
@@ -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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef GRAPHEME_GRAPHEME_UTIL_H
+#define GRAPHEME_GRAPHEME_UTIL_H
+
+#include "php_intl.h"
+#include "intl_convert.h"
+
+/* get_break_interator: get a break iterator from the global structure */
+UBreakIterator* grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status TSRMLS_DC );
+
+void
+grapheme_substr_ascii(char *str, int32_t str_len, int32_t f, int32_t l, int argc, char **sub_str, int *sub_str_len);
+
+int
+grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int f_ignore_case TSRMLS_DC);
+
+int
+grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int *puchar_pos, int f_ignore_case TSRMLS_DC);
+
+int grapheme_ascii_check(const unsigned char *day, int32_t len);
+
+int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_array[], int boundary_array_len TSRMLS_DC );
+
+inline int32_t
+grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len);
+
+inline int32_t
+grapheme_memnstr_grapheme(UBreakIterator *bi, UChar *haystack, UChar *needle, int32_t needle_len, UChar *end);
+
+inline void *grapheme_memrchr_grapheme(const void *s, int c, int32_t n);
+
+UChar *
+grapheme_get_haystack_offset(UBreakIterator* bi, UChar *uhaystack, int32_t uhaystack_len, int32_t offset);
+
+int32_t
+grapheme_strrpos_ascii(unsigned char *haystack, int32_t haystack_len, unsigned char *needle, int32_t needle_len, int32_t offset);
+
+UBreakIterator*
+grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status TSRMLS_DC );
+
+/* OUTSIDE_STRING: check if (possibly negative) long offset is outside the string with int32_t length */
+#define OUTSIDE_STRING(offset, max_len) ( offset < INT32_MIN || offset > INT32_MAX || (offset < 0 ? -offset > (long) max_len : offset >= (long) max_len) )
+
+#endif // GRAPHEME_GRAPHEME_UTIL_H
diff --git a/ext/intl/intl_common.h b/ext/intl/intl_common.h
new file mode 100755
index 0000000000..9424571d02
--- /dev/null
+++ b/ext/intl/intl_common.h
@@ -0,0 +1,44 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_COMMON_H
+#define INTL_COMMON_H
+/* Auxiliary macros */
+
+#include <php.h>
+#include <unicode/utypes.h>
+
+#ifndef UBYTES
+# define UBYTES(len) ((len) * sizeof(UChar))
+#endif
+
+#ifndef eumalloc
+# define eumalloc(size) (UChar*)safe_emalloc(size, sizeof(UChar), 0)
+#endif
+
+#ifndef eurealloc
+# define eurealloc(ptr, size) (UChar*)erealloc((ptr), size * sizeof(UChar))
+#endif
+
+#define USIZE(data) sizeof((data))/sizeof(UChar)
+#define UCHARS(len) ((len) / sizeof(UChar))
+
+#define INTL_Z_STRVAL_P(str) (UChar*) Z_STRVAL_P(str)
+#define INTL_Z_STRLEN_P(str) UCHARS( Z_STRLEN_P(str) )
+
+#endif /* INTL_COMMON_H */
diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c
new file mode 100755
index 0000000000..1d3d0e6028
--- /dev/null
+++ b/ext/intl/intl_convert.c
@@ -0,0 +1,154 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_convert.h"
+
+/* {{{ intl_convert_utf8_to_utf16
+ * Convert given string from UTF-8 to UTF-16 to *target buffer.
+ *
+ * It *target is NULL then we allocate a large enough buffer,
+ * store the converted string into it, and make target point to it.
+ *
+ * Otherwise, if *target is non-NULL, we assume that it points to a
+ * dynamically allocated buffer of *target_len bytes length.
+ * In this case the buffer will be used to store the converted string to,
+ * and may be resized (made larger) if needed.
+ *
+ * @param target Where to place the result.
+ * @param target_len Result length.
+ * @param source String to convert.
+ * @param source_len Length of the source string.
+ * @param status Conversion status.
+ *
+ * @return void This function does not return anything.
+ */
+void intl_convert_utf8_to_utf16(
+ UChar** target, int* target_len,
+ const char* src, int src_len,
+ UErrorCode* status )
+{
+ UChar* dst_buf = NULL;
+ int32_t dst_len = 0;
+
+ // If *target is NULL determine required destination buffer size (pre-flighting).
+ // Otherwise, attempt to convert source string; if *target buffer is not large enough
+ // it will be resized appropriately.
+ *status = U_ZERO_ERROR;
+
+ u_strFromUTF8( *target, *target_len, &dst_len, src, src_len, status );
+
+ if( *status == U_ZERO_ERROR )
+ {
+ // String is converted successfuly
+ (*target)[dst_len] = 0;
+ *target_len = dst_len;
+ return;
+ }
+
+ // Bail out if an unexpected error occured.
+ // (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).
+ if( *status != U_BUFFER_OVERFLOW_ERROR && *status != U_STRING_NOT_TERMINATED_WARNING )
+ return;
+
+ // Allocate memory for the destination buffer (it will be zero-terminated).
+ dst_buf = eumalloc( dst_len + 1 );
+
+ // Convert source string from UTF-8 to UTF-16.
+ *status = U_ZERO_ERROR;
+ u_strFromUTF8( dst_buf, dst_len+1, NULL, src, src_len, status );
+ if( U_FAILURE( *status ) )
+ {
+ efree( dst_buf );
+ return;
+ }
+
+ dst_buf[dst_len] = 0;
+
+ if( *target )
+ efree( *target );
+
+ *target = dst_buf;
+ *target_len = dst_len;
+}
+/* }}} */
+
+/* {{{ intl_convert_utf16_to_utf8
+ * Convert given string from UTF-16 to UTF-8.
+ *
+ * @param target Where to place the result.
+ * @param target_len Result length.
+ * @param source String to convert.
+ * @param source_len Length of the source string.
+ * @param status Conversion status.
+ *
+ * @return void This function does not return anything.
+ */
+void intl_convert_utf16_to_utf8(
+ char** target, int* target_len,
+ const UChar* src, int src_len,
+ UErrorCode* status )
+{
+ char* dst_buf = NULL;
+ int32_t dst_len;
+
+ // Determine required destination buffer size (pre-flighting).
+ *status = U_ZERO_ERROR;
+ u_strToUTF8( NULL, 0, &dst_len, src, src_len, status );
+
+ // Bail out if an unexpected error occured.
+ // (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).
+ if( *status != U_BUFFER_OVERFLOW_ERROR && *status != U_STRING_NOT_TERMINATED_WARNING )
+ return;
+
+ // Allocate memory for the destination buffer (it will be zero-terminated).
+ dst_buf = emalloc( dst_len+1 );
+
+ // Convert source string from UTF-8 to UTF-16.
+ *status = U_ZERO_ERROR;
+ u_strToUTF8( dst_buf, dst_len, NULL, src, src_len, status );
+ if( U_FAILURE( *status ) )
+ {
+ efree( dst_buf );
+ return;
+ }
+
+ // U_STRING_NOT_TERMINATED_WARNING is OK for us => reset 'status'.
+ *status = U_ZERO_ERROR;
+
+ dst_buf[dst_len] = 0;
+ *target = dst_buf;
+ *target_len = dst_len;
+}
+/* }}} */
+
+/*
+ * 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/intl_convert.h b/ext/intl/intl_convert.h
new file mode 100755
index 0000000000..3fc03f4bb7
--- /dev/null
+++ b/ext/intl/intl_convert.h
@@ -0,0 +1,33 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_CONVERT_H
+#define INTL_CONVERT_H
+
+#include <unicode/ustring.h>
+
+void intl_convert_utf8_to_utf16(
+ UChar** target, int* target_len,
+ const char* src, int src_len,
+ UErrorCode* status );
+
+void intl_convert_utf16_to_utf8(
+ char** target, int* target_len,
+ const UChar* src, int src_len,
+ UErrorCode* status );
+
+#endif // INTL_CONVERT_H
diff --git a/ext/intl/intl_data.h b/ext/intl/intl_data.h
new file mode 100755
index 0000000000..3f5205236e
--- /dev/null
+++ b/ext/intl/intl_data.h
@@ -0,0 +1,87 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_DATA_H
+#define INTL_DATA_H
+
+#include <unicode/utypes.h>
+
+#include "intl_error.h"
+
+/* Mock object to generalize error handling in sub-modules.
+ Sub-module data structures should always have error as first element
+ for this to work!
+*/
+typedef struct _intl_data {
+ zend_object zo;
+ intl_error error;
+} intl_object;
+
+#define INTL_METHOD_INIT_VARS(oclass, obj) \
+ zval* object = NULL; \
+ oclass##_object* obj = NULL; \
+ intl_error_reset( NULL TSRMLS_CC );
+
+#define INTL_DATA_ERROR(obj) (((intl_object *)(obj))->error)
+#define INTL_DATA_ERROR_P(obj) (&(INTL_DATA_ERROR((obj))))
+#define INTL_DATA_ERROR_CODE(obj) INTL_ERROR_CODE(INTL_DATA_ERROR((obj)))
+
+#define INTL_METHOD_FETCH_OBJECT(oclass, obj) \
+ obj = (oclass##_object *) zend_object_store_get_object( object TSRMLS_CC ); \
+ intl_error_reset( INTL_DATA_ERROR_P(obj) TSRMLS_CC ); \
+
+#define INTL_METHOD_CHECK_STATUS(obj, msg) \
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((obj)) TSRMLS_CC ); \
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((obj)) ) ) \
+ { \
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P((obj)), msg, 0 TSRMLS_CC ); \
+ RETURN_FALSE; \
+ }
+
+#define INTL_METHOD_RETVAL_UTF8(obj, ustring, ulen, free_it) \
+{ \
+ char *u8value; \
+ int u8len; \
+ intl_convert_utf16_to_utf8(&u8value, &u8len, ustring, ulen, &INTL_DATA_ERROR_CODE((obj))); \
+ if((free_it)) { \
+ efree(ustring); \
+ } \
+ INTL_METHOD_CHECK_STATUS((obj), "Error converting value to UTF-8"); \
+ RETVAL_STRINGL(u8value, u8len, 0); \
+}
+
+#define INTL_MAX_LOCALE_LEN 64
+
+#define INTL_CHECK_LOCALE_LEN(locale_len) \
+ if((locale_len) > INTL_MAX_LOCALE_LEN) { \
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Locale string too long, should be no longer than 64 characters", 0 TSRMLS_CC ); \
+ RETURN_NULL(); \
+ }
+
+#define INTL_CHECK_LOCALE_LEN_OBJ(locale_len, object) \
+ if((locale_len) > INTL_MAX_LOCALE_LEN) { \
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Locale string too long, should be no longer than 64 characters", 0 TSRMLS_CC ); \
+ zval_dtor(object); \
+ ZVAL_NULL(object); \
+ RETURN_NULL(); \
+ }
+
+
+#endif // INTL_DATA_H
diff --git a/ext/intl/intl_error.c b/ext/intl/intl_error.c
new file mode 100755
index 0000000000..7c1df8e17a
--- /dev/null
+++ b/ext/intl/intl_error.c
@@ -0,0 +1,215 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+
+#include "php_intl.h"
+#include "intl_error.h"
+
+ZEND_EXTERN_MODULE_GLOBALS( intl )
+
+/* {{{ intl_error* intl_g_error_get()
+ * Return global error structure.
+ */
+static intl_error* intl_g_error_get( TSRMLS_D )
+{
+ return &INTL_G( g_error );
+}
+/* }}} */
+
+/* {{{ void intl_free_custom_error_msg( intl_error* err )
+ * Free mem.
+ */
+static void intl_free_custom_error_msg( intl_error* err TSRMLS_DC )
+{
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return;
+
+ if( !err->free_custom_error_message )
+ return;
+
+ efree( err->custom_error_message );
+
+ err->custom_error_message = NULL;
+ err->free_custom_error_message = 0;
+}
+/* }}} */
+
+/* {{{ intl_error* intl_error_create()
+ * Create and initialize internals of 'intl_error'.
+ */
+intl_error* intl_error_create( TSRMLS_D )
+{
+ intl_error* err = ecalloc( 1, sizeof( intl_error ) );
+
+ intl_error_init( err TSRMLS_CC );
+
+ return err;
+}
+/* }}} */
+
+/* {{{ void intl_error_init( intl_error* coll_error )
+ * Initialize internals of 'intl_error'.
+ */
+void intl_error_init( intl_error* err TSRMLS_DC )
+{
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return;
+
+ err->code = U_ZERO_ERROR;
+ err->custom_error_message = NULL;
+ err->free_custom_error_message = 0;
+}
+/* }}} */
+
+/* {{{ void intl_error_reset( intl_error* err )
+ * Set last error code to 0 and unset last error message
+ */
+void intl_error_reset( intl_error* err TSRMLS_DC )
+{
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return;
+
+ err->code = U_ZERO_ERROR;
+
+ intl_free_custom_error_msg( err TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void intl_error_set_custom_msg( intl_error* err, char* msg, int copyMsg )
+ * Set last error message to msg copying it if needed.
+ */
+void intl_error_set_custom_msg( intl_error* err, char* msg, int copyMsg TSRMLS_DC )
+{
+ if( !msg )
+ return;
+
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return;
+
+ // Free previous message if any
+ intl_free_custom_error_msg( err TSRMLS_CC );
+
+ // Mark message copied if any
+ err->free_custom_error_message = copyMsg;
+
+ // Set user's error text message
+ err->custom_error_message = copyMsg ? estrdup( msg ) : msg;
+}
+/* }}} */
+
+/* {{{ const char* intl_error_get_message( intl_error* err )
+ * Create output message in format "<intl_error_text>: <extra_user_error_text>".
+ */
+char* intl_error_get_message( intl_error* err TSRMLS_DC )
+{
+ const char* uErrorName = NULL;
+ char* errMessage = 0;
+
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return estrdup( "" );
+
+ uErrorName = u_errorName( err->code );
+
+ // Format output string
+ if( err->custom_error_message )
+ {
+ spprintf( &errMessage, 0, "%s: %s", err->custom_error_message, uErrorName );
+ }
+ else
+ {
+ spprintf( &errMessage, 0, "%s", uErrorName );
+ }
+
+ return errMessage;
+}
+/* }}} */
+
+/* {{{ void intl_error_set_code( intl_error* err, UErrorCode err_code )
+ * Set last error code.
+ */
+void intl_error_set_code( intl_error* err, UErrorCode err_code TSRMLS_DC )
+{
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return;
+
+ err->code = err_code;
+}
+/* }}} */
+
+/* {{{ void intl_error_get_code( intl_error* err )
+ * Return last error code.
+ */
+UErrorCode intl_error_get_code( intl_error* err TSRMLS_DC )
+{
+ if( !err && !( err = intl_g_error_get( TSRMLS_C ) ) )
+ return U_ZERO_ERROR;
+
+ return err->code;
+}
+/* }}} */
+
+/* {{{ void intl_error_set( intl_error* err, UErrorCode code, char* msg, int copyMsg )
+ * Set error code and message.
+ */
+void intl_error_set( intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC )
+{
+ intl_error_set_code( err, code TSRMLS_CC );
+ intl_error_set_custom_msg( err, msg, copyMsg TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void intl_errors_reset( intl_error* err )
+ */
+void intl_errors_reset( intl_error* err TSRMLS_DC )
+{
+ intl_error_reset( err TSRMLS_CC );
+ intl_error_reset( NULL TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void intl_errors_set_custom_msg( intl_error* err, char* msg, int copyMsg )
+ */
+void intl_errors_set_custom_msg( intl_error* err, char* msg, int copyMsg TSRMLS_DC )
+{
+ intl_error_set_custom_msg( err, msg, copyMsg TSRMLS_CC );
+ intl_error_set_custom_msg( NULL, msg, copyMsg TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ intl_errors_set_code( intl_error* err, UErrorCode err_code )
+ */
+void intl_errors_set_code( intl_error* err, UErrorCode err_code TSRMLS_DC )
+{
+ intl_error_set_code( err, err_code TSRMLS_CC );
+ intl_error_set_code( NULL, err_code 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/intl_error.h b/ext/intl/intl_error.h
new file mode 100755
index 0000000000..5c469e1fca
--- /dev/null
+++ b/ext/intl/intl_error.h
@@ -0,0 +1,46 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_ERROR_H
+#define INTL_ERROR_H
+
+#include <unicode/utypes.h>
+
+#define INTL_ERROR_CODE(e) (e).code
+
+typedef struct _intl_error {
+ UErrorCode code;
+ char* custom_error_message;
+ int free_custom_error_message;
+} intl_error;
+
+intl_error* intl_error_create( TSRMLS_D );
+void intl_error_init( intl_error* err TSRMLS_DC );
+void intl_error_reset( intl_error* err TSRMLS_DC );
+void intl_error_set_code( intl_error* err, UErrorCode err_code TSRMLS_DC );
+void intl_error_set_custom_msg( intl_error* err, char* msg, int copyMsg TSRMLS_DC );
+void intl_error_set( intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC );
+UErrorCode intl_error_get_code( intl_error* err TSRMLS_DC );
+char* intl_error_get_message( intl_error* err TSRMLS_DC );
+
+// Wrappers to synchonize object's and global error structures.
+void intl_errors_reset( intl_error* err TSRMLS_DC );
+void intl_errors_set_custom_msg( intl_error* err, char* msg, int copyMsg TSRMLS_DC );
+void intl_errors_set_code( intl_error* err, UErrorCode err_code TSRMLS_DC );
+
+#endif // INTL_ERROR_H
diff --git a/ext/intl/locale/locale.c b/ext/intl/locale/locale.c
new file mode 100755
index 0000000000..fa56f5a66a
--- /dev/null
+++ b/ext/intl/locale/locale.c
@@ -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. |
+ +----------------------------------------------------------------------+
+ | Author: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "locale_class.h"
+#include "locale.h"
+
+#include <unicode/utypes.h>
+#include <unicode/uloc.h>
+#include <unicode/ustring.h>
+
+/* {{{ locale_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void locale_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Locale_ce_ptr )
+ {
+ zend_error( E_ERROR, "Locale class not defined" );
+ return;
+ }
+
+ #define LOCALE_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define LOCALE_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Locale_ce_ptr, ZEND_STRS( #x ) - 1, ULOC_##x TSRMLS_CC );
+ #define LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR(name, value) zend_declare_class_constant_string( Locale_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ LOCALE_EXPOSE_CLASS_CONST( ACTUAL_LOCALE );
+ LOCALE_EXPOSE_CLASS_CONST( VALID_LOCALE );
+
+ zend_declare_class_constant_null(Locale_ce_ptr, ZEND_STRS("DEFAULT_LOCALE") - 1 TSRMLS_CC);
+
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "LANG_TAG", LOC_LANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "EXTLANG_TAG", LOC_EXTLANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "SCRIPT_TAG", LOC_SCRIPT_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "REGION_TAG", LOC_REGION_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "VARIANT_TAG",LOC_VARIANT_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "GRANDFATHERED_LANG_TAG",LOC_GRANDFATHERED_LANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "PRIVATE_TAG",LOC_PRIVATE_TAG);
+
+ #undef LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR
+ #undef LOCALE_EXPOSE_CLASS_CONST
+ #undef LOCALE_EXPOSE_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/locale/locale.h b/ext/intl/locale/locale.h
new file mode 100755
index 0000000000..ad3f9c66a1
--- /dev/null
+++ b/ext/intl/locale/locale.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef LOCALE_LOCALE_H
+#define LOCALE_LOCALE_H
+
+#include <php.h>
+
+void locale_register_constants( INIT_FUNC_ARGS );
+
+#define OPTION_DEFAULT NULL
+#define LOC_LANG_TAG "language"
+#define LOC_SCRIPT_TAG "script"
+#define LOC_REGION_TAG "region"
+#define LOC_VARIANT_TAG "variant"
+#define LOC_EXTLANG_TAG "extlang"
+#define LOC_GRANDFATHERED_LANG_TAG "grandfathered"
+#define LOC_PRIVATE_TAG "private"
+#define LOC_CANONICALIZE_TAG "canonicalize"
+
+#define LOCALE_INI_NAME "intl.default_locale"
+
+#endif // LOCALE_LOCALE_H
diff --git a/ext/intl/locale/locale_class.c b/ext/intl/locale/locale_class.c
new file mode 100755
index 0000000000..329ce739dc
--- /dev/null
+++ b/ext/intl/locale/locale_class.c
@@ -0,0 +1,126 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <unicode/uloc.h>
+
+#include "php_intl.h"
+
+#include "intl_error.h"
+#include "locale_class.h"
+#include "locale_methods.h"
+#include "locale.h"
+
+zend_class_entry *Locale_ce_ptr = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// 'Locale' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ Locale methods arguments info */
+// NOTE: modifying 'locale_XX_args' do not forget to
+// modify approptiate 'locale_XX_args' for
+// the procedural API.
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_2_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_3_args, 0, 0, 3 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_4_args, 0, 0, 4 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ ZEND_ARG_INFO( 0, arg4 )
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ Locale_class_functions
+ * Every 'Locale' class method has an entry in this table
+ */
+
+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 )
+ ZEND_FENTRY( getScript, ZEND_FN( locale_get_script ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getRegion, ZEND_FN( locale_get_region ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getKeywords, ZEND_FN( locale_get_keywords ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getDisplayScript, ZEND_FN( locale_get_display_script ), locale_2_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getDisplayRegion, ZEND_FN( locale_get_display_region ), locale_2_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getDisplayName, ZEND_FN( locale_get_display_name ), locale_2_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getDisplayLanguage, ZEND_FN( locale_get_display_language ), locale_2_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getDisplayVariant, ZEND_FN( locale_get_display_variant ), locale_2_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( composeLocale, ZEND_FN( locale_compose ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( parseLocale, ZEND_FN( locale_parse ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getAllVariants, ZEND_FN( locale_get_all_variants ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( filterMatches, ZEND_FN( locale_filter_matches ), locale_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( lookup, ZEND_FN( locale_lookup ), locale_4_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( canonicalize, ZEND_FN( locale_canonicalize ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ locale_register_Locale_class
+ * Initialize 'Locale' class
+ */
+void locale_register_Locale_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ // Create and register 'Locale' class.
+ INIT_CLASS_ENTRY( ce, "Locale", Locale_class_functions );
+ ce.create_object = NULL;
+ Locale_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ // Declare 'Locale' class properties.
+ if( !Locale_ce_ptr )
+ {
+ zend_error( E_ERROR,
+ "Locale: Failed to register Locale class.");
+ return;
+ }
+}
+/* }}} */
+
+
+/*
+ * 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/locale/locale_class.h b/ext/intl/locale/locale_class.h
new file mode 100755
index 0000000000..94232f3540
--- /dev/null
+++ b/ext/intl/locale/locale_class.h
@@ -0,0 +1,46 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef LOCALE_CLASS_H
+#define LOCALE_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+
+#include <unicode/uloc.h>
+
+typedef struct {
+ zend_object zo;
+
+ // ICU locale
+ char* uloc1;
+
+} Locale_object;
+
+
+void locale_register_Locale_class( TSRMLS_D );
+
+extern zend_class_entry *Locale_ce_ptr;
+
+/* Auxiliary macros */
+
+#define LOCALE_METHOD_INIT_VARS \
+ zval* object = NULL; \
+ intl_error_reset( NULL TSRMLS_CC ); \
+
+#endif // #ifndef LOCALE_CLASS_H
diff --git a/ext/intl/locale/locale_methods.c b/ext/intl/locale/locale_methods.c
new file mode 100755
index 0000000000..1f6ca4d182
--- /dev/null
+++ b/ext/intl/locale/locale_methods.c
@@ -0,0 +1,1732 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include <unicode/udata.h>
+#include <unicode/putil.h>
+
+#include "php_intl.h"
+#include "locale.h"
+#include "locale_class.h"
+#include "locale_methods.h"
+#include "intl_convert.h"
+
+#include <zend_API.h>
+#include <zend.h>
+#include <php.h>
+#include "main/php_ini.h"
+
+ZEND_EXTERN_MODULE_GLOBALS( intl )
+
+//Sizes required for the strings "variant15" , "extlang11", "private12" etc.
+#define SEPARATOR "_"
+#define SEPARATOR1 "-"
+#define DELIMITER "-_"
+#define EXTLANG_PREFIX "a"
+#define PRIVATE_PREFIX "x"
+#define DISP_NAME "name"
+
+#define MAX_NO_VARIANT 15
+#define MAX_NO_EXTLANG 3
+#define MAX_NO_PRIVATE 15
+#define MAX_NO_LOOKUP_LANG_TAG 100
+
+//Sizes required for the strings "variant15" , "extlang3", "private12" etc.
+#define VARIANT_KEYNAME_LEN 11
+#define EXTLANG_KEYNAME_LEN 10
+#define PRIVATE_KEYNAME_LEN 11
+
+/* Based on IANA registry at the time of writing this code
+*
+*/
+static const char * const LOC_GRANDFATHERED[] = {
+ "art-lojban", "i-klingon", "i-lux", "i-navajo", "no-bok", "no-nyn",
+ "cel-gaulish", "en-GB-oed", "i-ami",
+ "i-bnn", "i-default", "i-enochian",
+ "i-mingo", "i-pwn", "i-tao",
+ "i-tay", "i-tsu", "sgn-BE-fr",
+ "sgn-BE-nl", "sgn-CH-de", "zh-cmn",
+ "zh-cmn-Hans", "zh-cmn-Hant", "zh-gan" ,
+ "zh-guoyu", "zh-hakka", "zh-min",
+ "zh-min-nan", "zh-wuu", "zh-xiang",
+ "zh-yue", NULL
+};
+
+/* Based on IANA registry at the time of writing this code
+* This array lists the preferred values for the grandfathered tags if applicable
+* This is in sync with the array LOC_GRANDFATHERED
+* e.g. the offsets of the grandfathered tags match the offset of the preferred value
+*/
+static const int LOC_PREFERRED_GRANDFATHERED_LEN = 6;
+static const char * const LOC_PREFERRED_GRANDFATHERED[] = {
+ "jbo", "tlh", "lb",
+ "nv", "nb", "nn",
+ NULL
+};
+
+/*returns TRUE if a is an ID separator FALSE otherwise*/
+#define isIDSeparator(a) (a == '_' || a == '-')
+#define isKeywordSeparator(a) (a == '@' )
+#define isEndOfTag(a) (a == '\0' )
+
+#define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
+
+/*returns TRUE if one of the special prefixes is here (s=string)
+ 'x-' or 'i-' */
+#define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
+#define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
+
+/* Dot terminates it because of POSIX form where dot precedes the codepage
+ * except for variant
+ */
+#define isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
+
+
+/*{{{ return the offset of 'key' in the array 'list'.
+ * returns -1 if not present
+ */
+static int16_t findOffset(const char* const* list, const char* key)
+{
+ const char* const* anchor = list;
+ while (*list != NULL) {
+ if (strcmp(key, *list) == 0) {
+ return (int16_t)(list - anchor);
+ }
+ list++;
+ }
+
+ return -1;
+
+}
+/*}}}*/
+
+static char* getPreferredTag(char* gf_tag)
+{
+ char* result = NULL;
+ int grOffset = 0;
+
+ grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
+ if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){
+ //return preferred tag
+ result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] );
+ } else {
+ //Return correct grandfathered language tag
+ result = estrdup( LOC_GRANDFATHERED[grOffset] );
+ }
+ return result;
+}
+
+/* {{{
+* returns the position of next token for lookup
+* or -1 if no token
+* strtokr equivalent search for token in reverse direction
+*/
+static int getStrrtokenPos(char* str, int savedPos)
+{
+ int result =-1;
+ int i=0;
+
+ for( i=savedPos; i>=0 ;i--){
+ if( isIDSeparator(*(str+i)) ){
+ //delimiter found; check for singleton
+ if( isIDSeparator(*(str+i-2)) ){
+ //a singleton; so send the position of token before the singleton
+ result = i-3;
+ } else {
+ result = i-1;
+ }
+ break;
+ }
+ }
+ if(result < 1){
+ //Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn'
+ result =-1;
+ }
+ return result;
+}
+/* }}} */
+
+/* {{{
+* returns the position of a singleton if present
+* returns -1 if no singleton
+* strtok equivalent search for singleton
+*/
+static int getSingletonPos(char* str)
+{
+ int result =-1;
+ int i=0;
+ int len = 0;
+
+ if( str && ((len=strlen(str))>0) ){
+ for( i=0; i<len ; i++){
+ if( isIDSeparator(*(str+i)) ){
+ if( i==1){
+ // string is of the form x-avy or a-prv1
+ result =0;
+ break;
+ } else {
+ //delimiter found; check for singleton
+ if( isIDSeparator(*(str+i+2)) ){
+ //a singleton; so send the position of separator before singleton
+ result = i+1;
+ break;
+ }
+ }
+ }
+ }//end of for
+
+ }
+ return result;
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getDefault( )
+ * Gets the default locale
+ }}} */
+/* {{{ proto static string locale_get_default( )
+ * Gets the default locale
+ */
+PHP_NAMED_FUNCTION( zif_locale_get_default){
+ if( INTL_G(default_locale) == NULL ) {
+ INTL_G(default_locale) = pestrdup( uloc_getDefault(), 1);
+ }
+ RETURN_STRING( INTL_G(default_locale), TRUE );
+}
+
+/* }}} */
+
+/* {{{ proto static string Locale::setDefault( string $locale )
+* sets the default locale
+* }}} */
+/* {{{ proto static string locale_set_default( string $locale )
+* sets the default locale
+*/
+PHP_NAMED_FUNCTION(zif_locale_set_default)
+{
+ char* locale_name = NULL;
+ int len=0;
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &locale_name ,&len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_set_default: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(len == 0) {
+ locale_name = (char *)uloc_getDefault() ;
+ len = strlen(locale_name);
+ }
+
+ zend_alter_ini_entry(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME), locale_name, len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{
+* Gets the value from ICU
+* common code shared by get_primary_language,get_script or get_region or get_variant
+* result = 0 if error, 1 if successful , -1 if no value
+*/
+static char* get_icu_value_internal( char* loc_name , char* tag_name, int* result , int fromParseLocale)
+{
+ char* tag_value = NULL;
+ int32_t tag_value_len = 512;
+
+ int singletonPos = 0;
+ char* mod_loc_name = NULL;
+ int grOffset = 0;
+
+ int32_t buflen = 512;
+ UErrorCode status = U_ZERO_ERROR;
+
+
+ if( tag_name != LOC_CANONICALIZE_TAG ){
+ //Handle grandfathered languages
+ grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
+ if( grOffset >= 0 ){
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ tag_value = estrdup(loc_name);
+ return tag_value;
+ } else {
+ //Since Grandfathered , no value , do nothing , retutn NULL
+ return NULL;
+ }
+ }
+
+ if( fromParseLocale==1 ){
+ //Handle singletons
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){
+ return loc_name;
+ }
+ }
+
+ singletonPos = getSingletonPos( loc_name );
+ if( singletonPos == 0){
+ //singleton at start of script, region , variant etc.
+ //or invalid singleton at start of language
+ return NULL;
+ } else if( singletonPos > 0 ){
+ //singleton at some position except at start
+ //strip off the singleton and rest of the loc_name
+ mod_loc_name = estrndup ( loc_name , singletonPos-1);
+ }
+ }//end of if fromParse
+
+ }//end of if != LOC_CANONICAL_TAG
+
+ if( mod_loc_name == NULL){
+ mod_loc_name = estrdup(loc_name );
+ }
+
+//Proceed to ICU
+ do{
+ tag_value = erealloc( tag_value , buflen );
+ tag_value_len = buflen;
+
+ if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
+ buflen = uloc_getScript ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
+ buflen = uloc_getLanguage ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
+ buflen = uloc_getCountry ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
+ buflen = uloc_getVariant ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
+ buflen = uloc_canonicalize ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+
+ if( U_FAILURE( status ) ) {
+ if( status == U_BUFFER_OVERFLOW_ERROR )
+ {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ //Error in retriving data
+ *result = 0;
+ if( tag_value ){
+ efree( tag_value );
+ }
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return NULL;
+ }
+
+ } while( buflen > tag_value_len );
+
+ if( buflen ==0 ){
+ //No value found
+ *result = -1;
+ if( tag_value ){
+ efree( tag_value );
+ }
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return NULL;
+ } else {
+ *result = 1;
+ }
+
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return tag_value;
+}
+/* }}} */
+
+/* {{{
+* Gets the value from ICU , called when PHP userspace function is called
+* common code shared by get_primary_language,get_script or get_region or get_variant
+*/
+static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
+{
+
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ char* tag_value = NULL;
+ char* empty_result = "";
+
+ int result = 0;
+ char* msg = NULL;
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name ,&loc_name_len ) == FAILURE) {
+ spprintf(&msg , 0, "locale_get_%s : unable to parse input params", tag_name );
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 TSRMLS_CC );
+ efree(msg);
+
+ RETURN_NULL();
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ //Call ICU get
+ tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
+
+ //No value found
+ if( result == -1 ) {
+ if( tag_value){
+ efree( tag_value);
+ }
+ RETURN_STRING( empty_result , TRUE);
+ }
+
+ //value found
+ if( tag_value){
+ RETURN_STRING( tag_value , FALSE);
+ }
+
+ //Error encountered while fetching the value
+ if( result ==0) {
+ spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
+ intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_NULL();
+ }
+
+}
+/* }}} */
+
+/* {{{
+ * proto public static string Locale::getScript($locale)
+ * gets the script for the $locale
+ }}} */
+/* {{{
+ * proto public static string locale_get_script($locale)
+ * gets the script for the $locale
+ */
+PHP_FUNCTION( locale_get_script )
+{
+ get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+ * proto public static string Locale::getRegion($locale)
+ * gets the region for the $locale
+ }}} */
+/* {{{
+ * proto public static string locale_get_region($locale)
+ * gets the region for the $locale
+ */
+PHP_FUNCTION( locale_get_region )
+{
+ get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+ * proto public static string Locale::getPrimaryLanguage($locale)
+ * gets the primary language for the $locale
+ }}} */
+/* {{{
+ * proto public static string locale_get_primary_language($locale)
+ * gets the primary language for the $locale
+ */
+PHP_FUNCTION(locale_get_primary_language )
+{
+ get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+
+/* {{{
+ * common code shared by display_xyz functions to get the value from ICU
+ }}} */
+static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ char* disp_loc_name = NULL;
+ int disp_loc_name_len = 0;
+
+ UChar* disp_name = NULL;
+ int32_t disp_name_len = 0;
+
+ char* mod_loc_name = NULL;
+
+ int32_t buflen = 512;
+ UErrorCode status = U_ZERO_ERROR;
+
+ char* utf8value = NULL;
+ int utf8value_len = 0;
+
+ char* msg = NULL;
+ int grOffset = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
+ &loc_name, &loc_name_len ,
+ &disp_loc_name ,&disp_loc_name_len ) == FAILURE)
+ {
+ spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name );
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ if( tag_name != DISP_NAME ){
+ //Handle grandfathered languages
+ grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
+ if( grOffset >= 0 ){
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ mod_loc_name = getPreferredTag( loc_name );
+ } else {
+ //Since Grandfathered , no value , do nothing , retutn NULL
+ RETURN_FALSE;
+ }
+ }
+
+/*
+ int singletonPos = 0;
+ //Handle singletons
+ if( (strcmp(tag_name , LOC_LANG_TAG)==0) && isIDPrefix(loc_name) ){
+ //return mod_loc_name;
+ } else {
+ singletonPos = getSingletonPos( loc_name );
+ if( singletonPos == 0){
+ //singleton at start of script, region , variant etc.
+ //or invalid singleton at start of language
+ RETURN_FALSE;
+ }else if(singletonPos > 0){
+ //singleton at some position except at start
+ //strip off the singleton and rest of the loc_name
+ mod_loc_name = estrndup( loc_name , singletonPos-1);
+ }
+ }
+*/
+ }//end of if != LOC_CANONICAL_TAG
+
+ if( mod_loc_name==NULL ){
+ mod_loc_name = estrdup( loc_name );
+ }
+
+ //Get the disp_value for the given locale
+ do{
+ disp_name = erealloc( disp_name , buflen );
+ disp_name_len = buflen;
+
+ //Check if disp_loc_name passed , if not use default locale
+ if( !disp_loc_name){
+ disp_loc_name = estrdup(INTL_G(default_locale));
+ }
+
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
+ } else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
+ buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
+ } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
+ buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
+ } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
+ buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
+ } else if( strcmp(tag_name , DISP_NAME)==0 ){
+ buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
+ }
+
+ if( U_FAILURE( status ) )
+ {
+ if( status == U_BUFFER_OVERFLOW_ERROR )
+ {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name );
+ intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
+ efree(msg);
+ if( disp_name){
+ efree( disp_name );
+ }
+ if( mod_loc_name){
+ efree( mod_loc_name );
+ }
+ RETURN_FALSE;
+ }
+ } while( buflen > disp_name_len );
+
+ if( mod_loc_name){
+ efree( mod_loc_name );
+ }
+ // Convert display locale name from UTF-16 to UTF-8.
+ intl_convert_utf16_to_utf8( &utf8value, &utf8value_len, disp_name, buflen, &status );
+ efree( disp_name );
+ if( U_FAILURE( status ) )
+ {
+ spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
+ intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_FALSE;
+ }
+
+ RETVAL_STRINGL( utf8value, utf8value_len , FALSE);
+
+}
+/* }}} */
+
+/* {{{
+* public static string Locale::getDisplayName($locale, $in_locale = null)
+* gets the name for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* public static string get_display_name($locale, $in_locale = null)
+* gets the name for the $locale in $in_locale or default_locale
+*/
+PHP_FUNCTION(locale_get_display_name)
+{
+ get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+* public static string Locale::getDisplayLanguage($locale, $in_locale = null)
+* gets the language for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* public static string get_display_language($locale, $in_locale = null)
+* gets the language for the $locale in $in_locale or default_locale
+*/
+PHP_FUNCTION(locale_get_display_language)
+{
+ get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+* public static string Locale::getDisplayScript($locale, $in_locale = null)
+* gets the script for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* public static string get_display_script($locale, $in_locale = null)
+* gets the script for the $locale in $in_locale or default_locale
+*/
+PHP_FUNCTION(locale_get_display_script)
+{
+ get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+* public static string Locale::getDisplayRegion($locale, $in_locale = null)
+* gets the region for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* public static string get_display_region($locale, $in_locale = null)
+* gets the region for the $locale in $in_locale or default_locale
+*/
+PHP_FUNCTION(locale_get_display_region)
+{
+ get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{
+* public static string Locale::getDisplayVariant($locale, $in_locale = null)
+* gets the variant for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* public static string get_display_variant($locale, $in_locale = null)
+* gets the variant for the $locale in $in_locale or default_locale
+*/
+PHP_FUNCTION(locale_get_display_variant)
+{
+ get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+ /* {{{
+ * proto static string[] getKeywords(string $locale) {
+ * return an associative array containing keyword-value
+ * pairs for this locale. The keys are keys to the array (doh!)
+ * }}}*/
+ /* {{{
+ * proto static string[] locale_get_keywords(string $locale) {
+ * return an associative array containing keyword-value
+ * pairs for this locale. The keys are keys to the array (doh!)
+ */
+
+PHP_FUNCTION( locale_get_keywords )
+{
+ UEnumeration* e = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+
+ const char* kw_key = NULL;
+ int32_t kw_key_len = 0;
+
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+/*
+ ICU expects the buffer to be allocated before calling the function
+ and so the buffer size has been explicitly specified
+ ICU uloc.h #define ULOC_KEYWORD_AND_VALUES_CAPACITY 100
+ hence the kw_value buffer size is 100
+*/
+ char* kw_value = NULL;
+ int32_t kw_value_len = 100;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_get_keywords: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ //Get the keywords
+ e = uloc_openKeywords( loc_name, &status );
+ if( e != NULL )
+ {
+ // Traverse it, filling the return array.
+ array_init( return_value );
+
+ while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
+ kw_value = ecalloc( 1 , kw_value_len );
+
+ //Get the keyword value for each keyword
+ kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len , &status );
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ status = U_ZERO_ERROR;
+ kw_value = erealloc( kw_value , kw_value_len+1);
+ kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len+1 , &status );
+ } else if(!U_FAILURE(status)) {
+ kw_value = erealloc( kw_value , kw_value_len+1);
+ }
+ if (U_FAILURE(status)) {
+ intl_error_set( NULL, FAILURE, "locale_get_keywords: Error encountered while getting the keyword value for the keyword", 0 TSRMLS_CC );
+ if( kw_value){
+ efree( kw_value );
+ }
+ zval_dtor(return_value);
+ RETURN_FALSE;
+ }
+
+ add_assoc_stringl( return_value, (char *)kw_key, kw_value , kw_value_len, 0);
+ } //end of while
+
+ }//end of if e!=NULL
+
+ uenum_close( e );
+}
+/* }}} */
+
+ /* {{{
+ * proto static string Locale::canonicalize($locale)
+ * @return string the canonicalized locale
+ * }}} */
+ /* {{{
+ * proto static string locale_canonicalize(Locale $loc, string $locale)
+ * @param string $locale The locale string to canonicalize
+ */
+PHP_FUNCTION(locale_canonicalize)
+{
+ get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+
+/* {{{ append_key_value
+* Internal function which is called from locale_compose
+* gets the value for the key_name and appends to the loc_name
+* returns 1 if successful , -1 if not found ,
+* 0 if array element is not a string , -2 if buffer-overflow
+*/
+static int append_key_value(char* loc_name, int loc_name_capacity , HashTable* hash_arr, char* key_name TSRMLS_DC)
+{
+ int needed_size = -1;
+ zval** ele_value = NULL;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ){
+ if( Z_TYPE_PP(ele_value)!= IS_STRING ){
+ //element value is not a string
+ return 0;
+ }
+ if( strcmp(key_name , LOC_LANG_TAG) != 0 &&
+ strcmp(key_name , LOC_GRANDFATHERED_LANG_TAG)!=0 ){
+
+ needed_size = Z_STRLEN_PP(ele_value)+1 ;
+ if( needed_size > loc_name_capacity ){
+ //Will cause Buffer_overflow
+ return -2;
+
+ } else {
+ strcat( loc_name , SEPARATOR);
+ strncat( loc_name ,
+ Z_STRVAL_PP(ele_value) ,
+ Z_STRLEN_PP(ele_value) );
+ }
+ } else {
+ //lang or grandfathered tag
+ needed_size = Z_STRLEN_PP(ele_value) ;
+ if( needed_size > loc_name_capacity ){
+ //Will cause Buffer_overflow
+ return -2;
+
+ } else {
+ strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) );
+ }
+ }
+
+ return 1;
+ }
+
+ return -1;
+}
+/* }}} */
+
+/* {{{ append_prefix , appends the prefix needed
+* e.g. private adds 'x'
+*/
+static void add_prefix(char* loc_name , char* key_name)
+{
+ if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
+ strcat( loc_name , SEPARATOR);
+ strcat( loc_name , PRIVATE_PREFIX);
+ }
+}
+/* }}} */
+
+/* {{{ append_multiple_key_values
+* Internal function which is called from locale_compose
+* gets the multiple values for the key_name and appends to the loc_name
+* used for 'variant','extlang','private'
+* returns 1 if successful , -1 if not found ,
+* 0 if array element is not a string , -2 if buffer-overflow
+*/
+static int append_multiple_key_values(char* loc_name, int loc_name_capacity , HashTable* hash_arr, char* key_name TSRMLS_DC)
+{
+ int result = -1;
+ zval** ele_value = NULL;
+ char* cur_key_name = NULL;
+
+ int i = 0;
+ int isFirstSubtag = 0;
+ int max_value = 0;
+ int needed_size = 0;
+
+ //Variant/ Extlang/Private etc.
+ if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ){
+ if( Z_TYPE_PP(ele_value)!= IS_STRING ){
+ //key_name is not a string
+ return 0;
+ }
+
+ //Determine the needed_size and check it against available
+ if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
+ //for the string "-x-" and the size of array element
+ needed_size = Z_STRLEN_PP(ele_value) + 3;
+ } else {
+ //for the SEPARATOR and the size of array element
+ needed_size = Z_STRLEN_PP(ele_value) + 1;
+ }
+ if( needed_size > loc_name_capacity){
+ //Will cause buffer_overflow
+ return -2;
+ }
+
+ add_prefix( loc_name , key_name);
+
+ strcat( loc_name , SEPARATOR);
+ strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) );
+ return 1;
+ } else {
+ //Decide the max_value : the max. no. of elements allowed
+ if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
+ max_value = MAX_NO_VARIANT;
+ }
+ if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
+ max_value = MAX_NO_EXTLANG;
+ }
+ if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
+ max_value = MAX_NO_PRIVATE;
+ }
+
+ //Multiple variant values as variant0, variant1 ,variant2
+ cur_key_name = (char*)ecalloc( 25, 25);
+ isFirstSubtag = 0;
+ for( i=0 ; i< max_value; i++ ){
+ sprintf( cur_key_name , "%s%d", key_name , i);
+ if( zend_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS ){
+ if( Z_TYPE_PP(ele_value)!= IS_STRING ){
+ //variant is not a string
+ if( cur_key_name){
+ efree(cur_key_name);
+ }
+ return 0;
+ }
+
+ //Determine the needed_size and check it against available
+ if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 && isFirstSubtag ==0 ){
+ //for the string "-x-" and the size of array element
+ needed_size = Z_STRLEN_PP(ele_value) + 3;
+ } else {
+ //for the SEPARATOR and the size of array element
+ needed_size = Z_STRLEN_PP(ele_value) + 1;
+ }
+ if( needed_size > loc_name_capacity){
+ //Will cause buffer_overflow
+ if( cur_key_name){
+ efree(cur_key_name);
+ }
+ return -2;
+ }
+
+ //Add the contents
+ if (isFirstSubtag++ == 0){
+ add_prefix( loc_name , cur_key_name);
+ }
+ strcat( loc_name , SEPARATOR);
+ strncat( loc_name , Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value) );
+ }
+ result = 1;
+ }//end of for
+ efree(cur_key_name);
+ }//end of else
+
+ return result;
+}
+/* }}} */
+
+/*{{{
+* If applicable sets error message and aborts locale_compose gracefully
+* returns 0 if locale_compose needs to be aborted
+* otherwise returns 1
+*/
+static int handleAppendResult( int result,char* loc_name TSRMLS_DC)
+{
+ intl_error_reset( NULL TSRMLS_CC );
+ if( result == 0 ){
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "Aborting locale_compose: parameter array element is not a string ", 0 TSRMLS_CC );
+
+ if( loc_name){
+ efree(loc_name);
+ }
+ return 0;
+ }
+ if( result == -2 ){
+ intl_error_set( NULL, U_BUFFER_OVERFLOW_ERROR,
+ "Aborting locale_compose: array element will cause the buffer overflow. Maximum size allowed for locale_compose parameters is 512 bytes including separator character and prefixes. ", 0 TSRMLS_CC );
+
+ if( loc_name){
+ efree(loc_name);
+ }
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{
+* proto static string Locale::composeLocale($array)
+* Creates a locale by combining the parts of locale-ID passed
+* }}} */
+/* {{{
+* proto static string compose_locale($array)
+* Creates a locale by combining the parts of locale-ID passed
+* }}} */
+PHP_FUNCTION(locale_compose)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ int32_t buflen = 512;
+
+ zval* arr = NULL;
+ HashTable* hash_arr = NULL;
+
+ int result = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "a",
+ &arr) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_compose: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ hash_arr = HASH_OF( arr );
+
+ if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
+ RETURN_FALSE;
+
+ //Allocate memory
+ loc_name = (char*)ecalloc( 512, sizeof(char));
+ loc_name_len = buflen;
+
+ //Check for grandfathered first
+ result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_GRANDFATHERED_LANG_TAG TSRMLS_CC );
+ if( result == 1 ){
+ RETURN_STRING( loc_name ,FALSE);
+ }
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Not grandfathered
+ result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_LANG_TAG TSRMLS_CC );
+ if( result == -1 ){
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_compose: parameter array does not contain 'language' tag.", 0 TSRMLS_CC );
+ if( loc_name){
+ efree(loc_name);
+ }
+ RETURN_FALSE;
+ }
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Extlang
+ result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_EXTLANG_TAG TSRMLS_CC );
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Script
+ result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_SCRIPT_TAG TSRMLS_CC );
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Region
+ result = append_key_value( loc_name , loc_name_len , hash_arr , LOC_REGION_TAG TSRMLS_CC );
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Variant
+ result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_VARIANT_TAG TSRMLS_CC );
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ //Private
+ result = append_multiple_key_values( loc_name , loc_name_len , hash_arr , LOC_PRIVATE_TAG TSRMLS_CC );
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING( loc_name , FALSE);
+}
+/* }}} */
+
+
+/*{{{
+* Parses the locale and returns private subtags if existing
+* else returns NULL
+* e.g. for locale='en_US-x-prv1-prv2-prv3'
+* returns a pointer to the string 'prv1-prv2-prv3'
+*/
+static char* get_private_subtags(char* loc_name)
+{
+ char* result =NULL;
+ int singletonPos = 0;
+ int len =0;
+ char* mod_loc_name =NULL;
+
+ if( loc_name && (len = strlen(loc_name)>0 ) ){
+ mod_loc_name = loc_name ;
+ len = strlen(mod_loc_name);
+ while( (singletonPos = getSingletonPos(mod_loc_name))!= -1){
+
+ if( singletonPos!=-1){
+ if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
+ //private subtag start found
+ if( singletonPos + 2 == len){
+ //loc_name ends with '-x-' ; return NULL
+ }
+ else{
+ //result = mod_loc_name + singletonPos +2;
+ result = estrndup(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ) );
+ }
+ break;
+ }
+ else{
+ if( singletonPos + 1 >= len){
+ //String end
+ break;
+ } else {
+ //singleton found but not a private subtag , hence check further in the string for the private subtag
+ mod_loc_name = mod_loc_name + singletonPos +1;
+ len = strlen(mod_loc_name);
+ }
+ }
+ }
+
+ }//end of while
+ }
+
+ return result;
+}
+/* }}} */
+
+/* {{{ code used by locale_parse
+*/
+static int add_array_entry(char* loc_name, zval* hash_arr, char* key_name TSRMLS_DC)
+{
+ char* key_value = NULL;
+ char* cur_key_name = NULL;
+ char* token = NULL;
+ char* last_ptr = NULL;
+
+ int result = 0;
+ int cur_result = 0;
+ int cnt = 0;
+
+
+ if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
+ key_value = get_private_subtags( loc_name );
+ result = 1;
+ } else {
+ key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
+ }
+ if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) ||
+ ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
+ if( result > 0 && key_value){
+ //Tokenize on the "_" or "-"
+ token = php_strtok_r( key_value , DELIMITER ,&last_ptr);
+ if( cur_key_name ){
+ efree( cur_key_name);
+ }
+ cur_key_name = (char*)ecalloc( 25, 25);
+ sprintf( cur_key_name , "%s%d", key_name , cnt++);
+ add_assoc_string( hash_arr, cur_key_name , token ,TRUE );
+ //tokenize on the "_" or "-" and stop at singleton if any
+ while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
+ sprintf( cur_key_name , "%s%d", key_name , cnt++);
+ add_assoc_string( hash_arr, cur_key_name , token , TRUE );
+ }
+
+ if( key_name == LOC_PRIVATE_TAG ){
+ }
+ }
+ } else {
+ if( result == 1 ){
+ add_assoc_string( hash_arr, key_name , key_value , TRUE );
+ cur_result = 1;
+ }
+ }
+
+ if( cur_key_name ){
+ efree( cur_key_name);
+ }
+ //if( key_name != LOC_PRIVATE_TAG && key_value){
+ if( key_value){
+ efree(key_value);
+ }
+ return cur_result;
+}
+
+/* {{{
+* proto static array Locale::parseLocale($locale)
+* parses a locale-id into an array the different parts of it
+ }}} */
+/* {{{
+* proto static array parse_locale($locale)
+* parses a locale-id into an array the different parts of it
+*/
+PHP_FUNCTION(locale_parse)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+ int grOffset = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ array_init( return_value );
+
+ grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
+ if( grOffset >= 0 ){
+ add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG , estrdup(loc_name) ,FALSE );
+ }
+ else{
+ //Not grandfathered
+ add_array_entry( loc_name , return_value , LOC_LANG_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_REGION_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_VARIANT_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ proto static array Locale::getAllVariants($locale)
+* gets an array containing the list of variants, or null
+ }}} */
+/* {{{ proto static array locale_get_all_variants($locale)
+* gets an array containing the list of variants, or null
+*/
+PHP_FUNCTION(locale_get_all_variants)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ int result = 0;
+ char* token = NULL;
+ char* variant = NULL;
+ char* saved_ptr = NULL;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+
+ array_init( return_value );
+
+ //If the locale is grandfathered , stop , no variants
+ if( findOffset( LOC_GRANDFATHERED , loc_name ) >= 0 ){
+ //("Grandfathered Tag. No variants.");
+ }
+ else {
+ //Call ICU variant
+ variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
+ if( result > 0 && variant){
+ //Tokenize on the "_" or "-"
+ token = php_strtok_r( variant , DELIMITER , &saved_ptr);
+ add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
+ //tokenize on the "_" or "-" and stop at singleton if any
+ while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
+ add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
+ }
+ }
+ if( variant ){
+ efree( variant );
+ }
+ }
+
+
+}
+/* }}} */
+
+/*{{{
+* Converts to lower case and also replaces all hyphuns with the underscore
+*/
+static int strToMatch(char* str ,char *retstr)
+{
+ char* anchor = NULL;
+ char* anchor1 = NULL;
+ int result = 0;
+ int len = 0;
+
+ if( (!str) || strlen(str) ==0){
+ return result;
+ } else {
+ anchor = retstr;
+ anchor1 = str;
+ len = strlen(str);
+ while( (*str)!='\0' ){
+ if( *str == '-' ){
+ *retstr = '_';
+ } else {
+ *retstr = tolower(*str);
+ }
+ str++;
+ retstr++;
+ }
+ *retstr = '\0';
+ retstr= anchor;
+ str= anchor1;
+ result = 1;
+ }
+
+ return(result);
+}
+/* }}} */
+
+/*{{{
+* code used by locale_filter_maatches
+*/
+PHP_FUNCTION(locale_filter_matches)
+{
+ char* lang_tag = NULL;
+ int lang_tag_len = 0;
+ char* loc_range = NULL;
+ int loc_range_len = 0;
+
+ int result = 0;
+ char* token = 0;
+ char* chrcheck = NULL;
+
+ char* can_lang_tag = NULL;
+ char* can_loc_range = NULL;
+
+ char* cur_lang_tag = NULL;
+ char* cur_loc_range = NULL;
+
+ zend_bool boolCanonical = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ssb",
+ &lang_tag, &lang_tag_len , &loc_range , &loc_range_len ,
+ &boolCanonical) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_filter_matches: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_range_len == 0) {
+ loc_range = INTL_G(default_locale);
+ }
+
+ if( strcmp(loc_range,"*")==0){
+ RETURN_TRUE;
+ }
+
+ if( boolCanonical ){
+ //canonicalize loc_range
+ can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
+ if( result ==0) {
+ intl_error_set( NULL, status,
+ "locale_filter_matches : unable to canonicalize loc_range" , 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ //canonicalize lang_tag
+ can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result , 0);
+ if( result ==0) {
+ intl_error_set( NULL, status,
+ "locale_filter_matches : unable to canonicalize lang_tag" , 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ //Convert to lower case for case-insensitive comparison
+ cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
+
+ //Convert to lower case for case-insensitive comparison
+ result = strToMatch( can_lang_tag , cur_lang_tag);
+ if( result == 0) {
+ efree( cur_lang_tag );
+ efree( can_lang_tag );
+ RETURN_FALSE;
+ }
+
+ cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
+ result = strToMatch( can_loc_range , cur_loc_range );
+ if( result == 0) {
+ efree( cur_lang_tag );
+ efree( can_lang_tag );
+ efree( cur_loc_range );
+ efree( can_loc_range );
+ RETURN_FALSE;
+ }
+
+ //check if prefix
+ token = strstr( cur_lang_tag , cur_loc_range );
+
+ if( token && (token==cur_lang_tag) ){
+ //check if the char. after match is SEPARATOR
+ chrcheck = token + (strlen(cur_loc_range));
+ if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ if( can_lang_tag){
+ efree( can_lang_tag );
+ }
+ if( can_loc_range){
+ efree( can_loc_range );
+ }
+ RETURN_TRUE;
+ }
+ }
+
+ //No prefix as loc_range
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ if( can_lang_tag){
+ efree( can_lang_tag );
+ }
+ if( can_loc_range){
+ efree( can_loc_range );
+ }
+ RETURN_FALSE;
+
+ }//end of if isCanonical
+ else{
+ //Convert to lower case for case-insensitive comparison
+ cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
+
+ result = strToMatch( lang_tag , cur_lang_tag);
+ if( result == 0) {
+ efree( cur_lang_tag );
+ RETURN_FALSE;
+ }
+ cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
+ result = strToMatch( loc_range , cur_loc_range );
+ if( result == 0) {
+ efree( cur_lang_tag );
+ efree( cur_loc_range );
+ RETURN_FALSE;
+ }
+
+ //check if prefix
+ token = strstr( cur_lang_tag , cur_loc_range );
+
+ if( token && (token==cur_lang_tag) ){
+ //check if the char. after match is SEPARATOR
+ chrcheck = token + (strlen(cur_loc_range));
+ if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ RETURN_TRUE;
+ }
+ }
+
+ //No prefix as loc_range
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ RETURN_FALSE;
+
+ }
+}
+
+static void array_cleanup( char* arr[] , int arr_size)
+{
+ int i=0;
+ for( i=0; i< arr_size; i++ ){
+ if( arr[i] ){
+ efree( arr[i]);
+ }
+ }
+
+}
+
+/* {{{
+* returns the lookup result to lookup_loc_range_src_php
+* internal function
+*/
+static char* lookup_loc_range(char* loc_range, HashTable* hash_arr , int isCanonical TSRMLS_DC)
+{
+ int cur_arr_ind = 0;
+ int i = 0;
+ int cur_arr_len = 0;
+ int result = 0;
+
+ char* lang_tag = NULL;
+ zval** ele_value = NULL;
+ char* cur_arr[MAX_NO_LOOKUP_LANG_TAG] ;
+
+ char* loc_range_to_cmp = NULL;
+ char* cur_loc_range = NULL;
+ char* can_loc_range = NULL;
+ int saved_pos = 0;
+
+ char* return_value = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ char* empty_result = "";
+
+ //convert the array to lowercase , also replace hyphuns with the underscore and store it in cur_arr
+ for(zend_hash_internal_pointer_reset(hash_arr);
+ zend_hash_has_more_elements(hash_arr) == SUCCESS;
+ zend_hash_move_forward(hash_arr)) {
+
+ if (zend_hash_get_current_data(hash_arr, (void**)&ele_value) == FAILURE) {
+ // Should never actually fail
+ // since the key is known to exist.
+ continue;
+ }
+ if( Z_TYPE_PP(ele_value)!= IS_STRING ){
+ //element value is not a string
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "lookup_loc_range: array element is not a string ",
+ 0 TSRMLS_CC );
+ return NULL;
+ } else {
+ if( lang_tag ){
+ efree( lang_tag );
+ }
+ lang_tag = estrdup(Z_STRVAL_PP(ele_value) );
+
+ if( isCanonical ==0 ){
+ //+1 for the terminating '\0'
+ cur_arr[cur_arr_ind] = ecalloc(1, strlen(lang_tag)+1 );
+ result = strToMatch(lang_tag, cur_arr[cur_arr_ind]) ;
+ }
+ else{
+ cur_arr[cur_arr_ind] = estrdup(lang_tag);
+ }
+
+
+ if( cur_arr_ind < MAX_NO_LOOKUP_LANG_TAG ){
+ cur_arr_ind++ ;
+ }
+ else{
+ break;
+ }
+ }
+ }//end of for
+ if( lang_tag ){
+ efree( lang_tag );
+ }
+
+ cur_arr_len = cur_arr_ind;
+
+ //Canonicalize array elements
+ if( isCanonical ==1 ){
+ for( i=0; i< cur_arr_ind; i++ ){
+ lang_tag =get_icu_value_internal( cur_arr[i] , LOC_CANONICALIZE_TAG , &result , 0);
+ efree( cur_arr[i] );
+ cur_arr[i] = ecalloc(1, strlen(lang_tag)+1 );
+ result = strToMatch(lang_tag, cur_arr[i]) ;
+ efree( lang_tag);
+ if( result ==0) {
+ intl_error_set( NULL, status,
+ "locale_lookup : unable to canonicalize lang_tag" , 0 TSRMLS_CC );
+ if( lang_tag ){
+ efree( lang_tag );
+ }
+ array_cleanup( cur_arr , cur_arr_len );
+ return NULL;
+ }
+ }
+
+ }
+
+ if( isCanonical ==1 ){
+ //Canonicalize the loc_range
+ can_loc_range =get_icu_value_internal( loc_range, LOC_CANONICALIZE_TAG , &result , 0);
+ if( result != 1 ){
+ //Error
+ intl_error_set( NULL, status,
+ "locale_lookup : unable to canonicalize loc_range" , 0 TSRMLS_CC );
+ if( lang_tag ){
+ efree( lang_tag );
+ }
+ if( can_loc_range ){
+ efree( can_loc_range );
+ }
+ array_cleanup( cur_arr , cur_arr_len );
+ return NULL;
+ } else {
+ //convert to lower and replace hyphuns
+ cur_loc_range = ecalloc( 1, strlen(can_loc_range) +1);
+ result = strToMatch(can_loc_range , cur_loc_range);
+ }
+ } else {
+ cur_loc_range = ecalloc( 1, strlen(loc_range) +1);
+ //convert to lower and replace hyphuns
+ result = strToMatch(loc_range , cur_loc_range);
+ }
+
+
+ //Lookup for the lang_tag match
+ saved_pos = strlen(cur_loc_range);
+ while(saved_pos!=(-1) ){
+ if( loc_range_to_cmp){
+ efree(loc_range_to_cmp);
+ }
+ loc_range_to_cmp = estrndup(cur_loc_range,saved_pos+1);
+ for( i=0; i< cur_arr_ind; i++ ){
+ if( cur_arr[i] && (strcmp(loc_range_to_cmp,cur_arr[i])==0) ){
+ //Match found
+ return_value = estrdup( cur_arr[i] );
+ if ( cur_loc_range ){
+ efree( cur_loc_range );
+ }
+ if( loc_range_to_cmp){
+ efree(loc_range_to_cmp);
+ }
+ if( can_loc_range ){
+ efree( can_loc_range );
+ }
+ array_cleanup( cur_arr , cur_arr_len );
+ return return_value;
+ }
+ }
+ saved_pos = getStrrtokenPos( cur_loc_range , saved_pos );
+ }
+
+ if( loc_range_to_cmp){
+ efree(loc_range_to_cmp);
+ }
+ if ( cur_loc_range ){
+ efree( cur_loc_range );
+ }
+ if( can_loc_range ){
+ efree( can_loc_range );
+ }
+ array_cleanup( cur_arr , cur_arr_len );
+ //Match not found
+ return empty_result;
+
+}
+/* }}} */
+
+/* {{{
+* public static function lookup(array $langtag, $locale, $default = null)
+* Searchs the items in $langtag for the best match to the language
+* range
+*/
+/* {{{
+* public static function lookup(array $langtag, $locale, $default = null)
+* Searchs the items in $langtag for the best match to the language
+* range
+*/
+PHP_FUNCTION(locale_lookup)
+{
+ char* fallback_loc = NULL;
+ int fallback_loc_len = 0;
+ char* loc_range = NULL;
+ int loc_range_len = 0;
+
+ zval* arr = NULL;
+ HashTable* hash_arr = NULL;
+ zend_bool boolCanonical = 0;
+
+ char* result =NULL;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "asb|s",
+ &arr,&loc_range,&loc_range_len,&boolCanonical,
+ &fallback_loc,&fallback_loc_len) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_lookup: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_NULL();
+ }
+
+
+ if(loc_range_len == 0) {
+ loc_range = INTL_G(default_locale);
+ }
+
+
+ //MAKE_STD_ZVAL(hash_arr);
+ hash_arr = HASH_OF( arr );
+
+ if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ){
+ RETURN_EMPTY_STRING();
+ }
+ else{
+ result = lookup_loc_range( loc_range ,hash_arr ,(boolCanonical?1:0) TSRMLS_CC);
+
+ if(result == NULL || result[0] == '\0') {
+ if( fallback_loc ) {
+ result = estrndup( fallback_loc , fallback_loc_len);
+ } else {
+ RETURN_EMPTY_STRING();
+ }
+ }
+}
+
+ RETVAL_STRINGL( result, strlen(result), 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
+ *can_loc_len
+*/
diff --git a/ext/intl/locale/locale_methods.h b/ext/intl/locale/locale_methods.h
new file mode 100755
index 0000000000..0b2db50f6c
--- /dev/null
+++ b/ext/intl/locale/locale_methods.h
@@ -0,0 +1,47 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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. |
+ +----------------------------------------------------------------------+
+ | Author: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef LOCALE_METHODS_H
+#define LOCALE_METHODS_H
+
+#include <php.h>
+
+
+PHP_FUNCTION( locale_get_primary_language );
+PHP_FUNCTION( locale_get_script );
+PHP_FUNCTION( locale_get_region );
+PHP_FUNCTION( locale_get_all_variants);
+
+PHP_NAMED_FUNCTION( zif_locale_get_default );
+PHP_NAMED_FUNCTION( zif_locale_set_default );
+
+PHP_FUNCTION( locale_get_display_name );
+PHP_FUNCTION( locale_get_display_language );
+PHP_FUNCTION( locale_get_display_script );
+PHP_FUNCTION( locale_get_display_region );
+PHP_FUNCTION( locale_get_display_variant );
+
+PHP_FUNCTION( locale_get_keywords );
+PHP_FUNCTION( locale_canonicalize);
+
+PHP_FUNCTION( locale_compose);
+PHP_FUNCTION( locale_parse);
+
+PHP_FUNCTION( locale_filter_matches);
+PHP_FUNCTION( locale_lookup);
+PHP_FUNCTION( locale_canonicalize);
+
+#endif // LOCALE_METHODS_H
diff --git a/ext/intl/msgformat/msgformat.c b/ext/intl/msgformat/msgformat.c
new file mode 100755
index 0000000000..1c792aa81a
--- /dev/null
+++ b/ext/intl/msgformat/msgformat.c
@@ -0,0 +1,256 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include <unicode/umsg.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "intl_convert.h"
+
+/* {{{ proto MessageFormatter MesssageFormatter::create( string $locale, string $pattern )
+ * Create formatter. }}} */
+/* {{{ proto MessageFormatter msgfmt_create( string $locale, string $pattern )
+ * Create formatter.
+ */
+PHP_FUNCTION( msgfmt_create )
+{
+ char* locale;
+ char* pattern;
+ int locale_len = 0, pattern_len = 0;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ zval* object;
+ MessageFormatter_object* mfo;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss",
+ &locale, &locale_len, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_create: unable to parse input parameters", 0 TSRMLS_CC );
+
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN(locale_len);
+ // Create a MessageFormatter object and save the ICU formatter into it.
+ if( ( object = getThis() ) == NULL )
+ object = return_value;
+
+ if( Z_TYPE_P( object ) != IS_OBJECT )
+ object_init_ex( object, MessageFormatter_ce_ptr );
+
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ // Convert pattern (if specified) to UTF-16.
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( mfo ),
+ "msgfmt_create: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_create: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+
+ (mfo)->mf_data.orig_format = estrndup(pattern, pattern_len);
+ (mfo)->mf_data.orig_format_len = pattern_len;
+
+ // Create an ICU message formatter.
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+
+ if(spattern) {
+ efree(spattern);
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( mfo ),
+ "msgfmt_create: message formatter creation failed", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto void MessageFormatter::__construct( string $locale, string $pattern )
+ * MessageFormatter object constructor.
+ */
+PHP_METHOD( MessageFormatter, __construct )
+{
+ char* locale;
+ char* pattern;
+ int locale_len, pattern_len = 0;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ zval* object;
+ MessageFormatter_object* mfo;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ object = getThis();
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss",
+ &locale, &locale_len, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "__construct: unable to parse input params", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, object);
+ mfo = (MessageFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ intl_error_reset( &mfo->mf_data.error TSRMLS_CC );
+
+ // Convert pattern to UTF-16.
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE( mfo ),
+ "__construct: Error converting pattern to UTF-16", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "__construct: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+
+ (mfo)->mf_data.orig_format = estrndup(pattern, pattern_len);
+ (mfo)->mf_data.orig_format_len = pattern_len;
+
+ // Create an ICU message formatter.
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+
+ if(spattern && spattern_len) {
+ efree(spattern);
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+ {
+ intl_error_set( NULL, INTL_DATA_ERROR_CODE(mfo),
+ "__construct: message formatter creation failed", 0 TSRMLS_CC );
+ zval_dtor(object);
+ ZVAL_NULL(object);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ proto int MessageFormatter::getErrorCode()
+ * Get formatter's last error code. }}} */
+/* {{{ proto int msgfmt_get_error_code( MessageFormatter $nf )
+ * Get formatter's last error code.
+ */
+PHP_FUNCTION( msgfmt_get_error_code )
+{
+ zval* object = NULL;
+ MessageFormatter_object* mfo = NULL;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, MessageFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ mfo = (MessageFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ // Return formatter's last error code.
+ RETURN_LONG( INTL_DATA_ERROR_CODE(mfo) );
+}
+/* }}} */
+
+/* {{{ proto string MessageFormatter::getErrorMessage( )
+ * Get text description for formatter's last error code. }}} */
+/* {{{ proto string msgfmt_get_error_message( MessageFormatter $coll )
+ * Get text description for formatter's last error code.
+ */
+PHP_FUNCTION( msgfmt_get_error_message )
+{
+ char* message = NULL;
+ zval* object = NULL;
+ MessageFormatter_object* mfo = NULL;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, MessageFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ mfo = (MessageFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ // Return last error message.
+ message = intl_error_get_message( &mfo->mf_data.error 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/msgformat/msgformat.h b/ext/intl/msgformat/msgformat.h
new file mode 100755
index 0000000000..205c7066fd
--- /dev/null
+++ b/ext/intl/msgformat/msgformat.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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMATTER_H
+#define MSG_FORMATTER_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_create );
+PHP_FUNCTION( msgfmt_get_error_code );
+PHP_FUNCTION( msgfmt_get_error_message );
+PHP_METHOD( MessageFormatter, __construct );
+
+#endif // MSG_FORMAT_H
diff --git a/ext/intl/msgformat/msgformat_attr.c b/ext/intl/msgformat/msgformat_attr.c
new file mode 100755
index 0000000000..62e797abca
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_attr.c
@@ -0,0 +1,144 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_attr.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+
+
+/* {{{ proto string MessageFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string msgfmt_get_pattern( MessageFormatter $mf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( msgfmt_get_pattern )
+{
+ UChar value_buf[64];
+ int length = USIZE( value_buf );
+ UChar* value = value_buf;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, MessageFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( INTL_DATA_ERROR_P(mfo), U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_get_pattern: unable to parse input params", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ if(mfo->mf_data.orig_format) {
+ RETURN_STRINGL(mfo->mf_data.orig_format, mfo->mf_data.orig_format_len, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool MessageFormatter::setPattern( string $pattern )
+ * Set formatter pattern. }}} */
+/* {{{ proto bool msgfmt_set_pattern( MessageFormatter $mf, string $pattern )
+ * Set formatter pattern.
+ */
+PHP_FUNCTION( msgfmt_set_pattern )
+{
+ char* value = NULL;
+ int value_len = 0;
+ int spattern_len = 0;
+ UChar* spattern = NULL;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, MessageFormatter_ce_ptr, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_set_pattern: unable to parse input params", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ }
+
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ // Convert given pattern to UTF-16.
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, value, value_len, &INTL_DATA_ERROR_CODE(mfo));
+ INTL_METHOD_CHECK_STATUS(mfo, "Error converting pattern to UTF-16" );
+
+ if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_set_pattern: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ // TODO: add parse error information
+ umsg_applyPattern(MSG_FORMAT_OBJECT(mfo), spattern, spattern_len, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ efree(spattern);
+ INTL_METHOD_CHECK_STATUS(mfo, "Error setting symbol value");
+
+ if(mfo->mf_data.orig_format) {
+ efree(mfo->mf_data.orig_format);
+ }
+ mfo->mf_data.orig_format = estrndup(value, value_len);
+ mfo->mf_data.orig_format_len = value_len;
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string MessageFormatter::getLocale()
+ * Get formatter locale. }}} */
+/* {{{ proto string msgfmt_get_locale(MessageFormatter $mf)
+ * Get formatter locale.
+ */
+PHP_FUNCTION( msgfmt_get_locale )
+{
+ char *loc;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, MessageFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ loc = (char *)umsg_getLocale(MSG_FORMAT_OBJECT(mfo));
+ RETURN_STRING(loc, 1);
+}
+/* }}} */
+
+/*
+ * 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/msgformat/msgformat_attr.h b/ext/intl/msgformat/msgformat_attr.h
new file mode 100755
index 0000000000..898c4451e1
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_attr.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_ATTR_H
+#define MSG_FORMAT_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_set_pattern );
+PHP_FUNCTION( msgfmt_get_pattern );
+PHP_FUNCTION( msgfmt_get_locale );
+
+#endif // MSG_FORMAT_ATTR_H
diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c
new file mode 100755
index 0000000000..853cbdb78a
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_class.c
@@ -0,0 +1,127 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <unicode/unum.h>
+
+#include "msgformat_class.h"
+#include "php_intl.h"
+#include "msgformat_data.h"
+#include "msgformat_format.h"
+#include "msgformat_parse.h"
+#include "msgformat.h"
+#include "msgformat_attr.h"
+
+zend_class_entry *MessageFormatter_ce_ptr = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// Auxiliary functions needed by objects of 'MessageFormatter' class
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ MessageFormatter_objects_dtor */
+static void MessageFormatter_object_dtor(void *object, zend_object_handle handle TSRMLS_DC )
+{
+ zend_objects_destroy_object( object, handle TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ MessageFormatter_objects_free */
+void MessageFormatter_object_free( zend_object *object TSRMLS_DC )
+{
+ MessageFormatter_object* mfo = (MessageFormatter_object*)object;
+
+ zend_object_std_dtor( &mfo->zo TSRMLS_CC );
+
+ msgformat_data_free( &mfo->mf_data TSRMLS_CC );
+
+ efree( mfo );
+}
+/* }}} */
+
+/* {{{ MessageFormatter_object_create */
+zend_object_value MessageFormatter_object_create(zend_class_entry *ce TSRMLS_DC)
+{
+ zend_object_value retval;
+ MessageFormatter_object* intern;
+
+ intern = ecalloc( 1, sizeof(MessageFormatter_object) );
+ msgformat_data_init( &intern->mf_data TSRMLS_CC );
+ zend_object_std_init( &intern->zo, ce TSRMLS_CC );
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ MessageFormatter_object_dtor,
+ (zend_objects_free_object_storage_t)MessageFormatter_object_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = zend_get_std_object_handlers();
+
+ return retval;
+}
+/* }}} */
+
+/////////////////////////////////////////////////////////////////////////////
+// 'MessageFormatter' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ MessageFormatter_class_functions
+ * Every 'MessageFormatter' class method has an entry in this table
+ */
+
+static function_entry MessageFormatter_class_functions[] = {
+ PHP_ME( MessageFormatter, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( msgfmt_create ), NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( format, ZEND_FN( msgfmt_format ), NULL )
+ ZEND_FENTRY( formatMessage, ZEND_FN( msgfmt_format_message ), NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( parse, ZEND_FN( msgfmt_parse ), NULL )
+ ZEND_FENTRY( parseMessage, ZEND_FN( msgfmt_parse_message ), NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( setPattern, ZEND_FN( msgfmt_set_pattern ), NULL )
+ PHP_NAMED_FE( getPattern, ZEND_FN( msgfmt_get_pattern ), NULL )
+ PHP_NAMED_FE( getLocale, ZEND_FN( msgfmt_get_locale ), NULL )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( msgfmt_get_error_code ), NULL )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( msgfmt_get_error_message ), NULL )
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ msgformat_register_class
+ * Initialize 'MessageFormatter' class
+ */
+void msgformat_register_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ // Create and register 'MessageFormatter' class.
+ INIT_CLASS_ENTRY( ce, "MessageFormatter", MessageFormatter_class_functions );
+ ce.create_object = MessageFormatter_object_create;
+ MessageFormatter_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ // Declare 'MessageFormatter' class properties.
+ if( !MessageFormatter_ce_ptr )
+ {
+ zend_error(E_ERROR, "Failed to register MessageFormatter class");
+ return;
+ }
+}
+/* }}} */
+
+/*
+ * 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/msgformat/msgformat_class.h b/ext/intl/msgformat/msgformat_class.h
new file mode 100755
index 0000000000..0afa792ae3
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_class.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_CLASS_H
+#define MSG_FORMAT_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+#include "intl_data.h"
+#include "msgformat_data.h"
+
+typedef struct {
+ zend_object zo;
+ msgformat_data mf_data;
+} MessageFormatter_object;
+
+void msgformat_register_class( TSRMLS_D );
+extern zend_class_entry *MessageFormatter_ce_ptr;
+
+/* Auxiliary macros */
+
+#define MSG_FORMAT_METHOD_INIT_VARS INTL_METHOD_INIT_VARS(MessageFormatter, mfo)
+#define MSG_FORMAT_METHOD_FETCH_OBJECT INTL_METHOD_FETCH_OBJECT(MessageFormatter, mfo)
+#define MSG_FORMAT_OBJECT(mfo) (mfo)->mf_data.umsgf
+
+#endif // #ifndef MSG_FORMAT_CLASS_H
diff --git a/ext/intl/msgformat/msgformat_data.c b/ext/intl/msgformat/msgformat_data.c
new file mode 100755
index 0000000000..aad36f594d
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_data.c
@@ -0,0 +1,98 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include "msgformat_data.h"
+
+/* {{{ void msgformat_data_init( msgformat_data* mf_data )
+ * Initialize internals of msgformat_data.
+ */
+void msgformat_data_init( msgformat_data* mf_data TSRMLS_DC )
+{
+ if( !mf_data )
+ return;
+
+ mf_data->umsgf = NULL;
+ mf_data->orig_format = NULL;
+ intl_error_reset( &mf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void msgformat_data_free( msgformat_data* mf_data )
+ * Clean up memory allocated for msgformat_data
+ */
+void msgformat_data_free( msgformat_data* mf_data TSRMLS_DC )
+{
+ if( !mf_data )
+ return;
+
+ if( mf_data->umsgf )
+ umsg_close( mf_data->umsgf );
+
+ if(mf_data->orig_format) {
+ efree(mf_data->orig_format);
+ mf_data->orig_format = NULL;
+ }
+
+ mf_data->umsgf = NULL;
+ intl_error_reset( &mf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ msgformat_data* msgformat_data_create()
+ * Allocate memory for msgformat_data and initialize it with default values.
+ */
+msgformat_data* msgformat_data_create( TSRMLS_D )
+{
+ msgformat_data* mf_data = ecalloc( 1, sizeof(msgformat_data) );
+
+ msgformat_data_init( mf_data TSRMLS_CC );
+
+ return mf_data;
+}
+/* }}} */
+
+int msfgotmat_fix_quotes(UChar **spattern, uint32_t *spattern_len, UErrorCode *ec)
+{
+ if(*spattern && *spattern_len && u_strchr(*spattern, (UChar)'\'')) {
+ UChar *npattern = emalloc(sizeof(UChar)*(2*(*spattern_len)+1));
+ uint32_t npattern_len;
+ npattern_len = umsg_autoQuoteApostrophe(*spattern, *spattern_len, npattern, 2*(*spattern_len)+1, ec);
+ efree(*spattern);
+ if( U_FAILURE(*ec) )
+ {
+ return FAILURE;
+ }
+ npattern = erealloc(npattern, sizeof(UChar)*(npattern_len+1));
+ *spattern = npattern;
+ *spattern_len = npattern_len;
+ }
+ return SUCCESS;
+}
+
+
+/*
+ * 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/msgformat/msgformat_data.h b/ext/intl/msgformat/msgformat_data.h
new file mode 100755
index 0000000000..6d7e8e1330
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_data.h
@@ -0,0 +1,41 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_DATA_H
+#define MSG_FORMAT_DATA_H
+
+#include <php.h>
+
+#include <unicode/umsg.h>
+
+#include "intl_error.h"
+
+typedef struct {
+ // error hangling
+ intl_error error;
+
+ // formatter handling
+ UMessageFormat* umsgf;
+ char* orig_format;
+ ulong orig_format_len;
+} msgformat_data;
+
+msgformat_data* msgformat_data_create( TSRMLS_D );
+void msgformat_data_init( msgformat_data* mf_data TSRMLS_DC );
+void msgformat_data_free( msgformat_data* mf_data TSRMLS_DC );
+int msfgotmat_fix_quotes(UChar **spattern, uint32_t *spattern_len, UErrorCode *ec);
+
+#endif // MSG_FORMAT_DATA_H
diff --git a/ext/intl/msgformat/msgformat_format.c b/ext/intl/msgformat/msgformat_format.c
new file mode 100755
index 0000000000..dc1c0abdb7
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_format.c
@@ -0,0 +1,184 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_format.h"
+#include "msgformat_data.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+
+#ifndef Z_ADDREF_P
+#define Z_ADDREF_P(z) ((z)->refcount++)
+#endif
+
+/* {{{ */
+static void msgfmt_do_format(MessageFormatter_object *mfo, zval *args, zval *return_value TSRMLS_DC)
+{
+ zval **fargs;
+ int count;
+ UChar* formatted = NULL;
+ int formatted_len = 0;
+ HashPosition pos;
+ int i;
+
+ count = zend_hash_num_elements(Z_ARRVAL_P(args));
+
+ if(count < umsg_format_arg_count(MSG_FORMAT_OBJECT(mfo))) {
+ // Not enough aguments for format!
+ intl_error_set( INTL_DATA_ERROR_P(mfo), U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format: not enough parameters", 0 TSRMLS_CC );
+ RETVAL_FALSE;
+ return;
+ }
+
+ fargs = safe_emalloc(count, sizeof(zval *), 0);
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(args), &pos);
+ for(i=0;i<count;i++) {
+ zval **val;
+ zend_hash_get_current_data_ex(Z_ARRVAL_P(args), (void **)&val, &pos);
+ fargs[i] = *val;
+ Z_ADDREF_P(fargs[i]);
+ /* TODO: needs refcount increase here? */
+ zend_hash_move_forward_ex(Z_ARRVAL_P(args), &pos);
+ }
+
+ umsg_format_helper(MSG_FORMAT_OBJECT(mfo), count, fargs, &formatted, &formatted_len, &INTL_DATA_ERROR_CODE(mfo) TSRMLS_CC);
+
+ for(i=0;i<count;i++) {
+ zval_ptr_dtor(&fargs[i]);
+ }
+
+ efree(fargs);
+
+ if (formatted && U_FAILURE( INTL_DATA_ERROR_CODE(mfo) ) ) {
+ efree(formatted);
+ }
+
+ INTL_METHOD_CHECK_STATUS( mfo, "Number formatting failed" );
+ INTL_METHOD_RETVAL_UTF8( mfo, formatted, formatted_len, 1 );
+}
+/* }}} */
+
+/* {{{ proto mixed MessageFormatter::format( array $args )
+ * Format a message. }}} */
+/* {{{ proto mixed msgfmt_format( MessageFormatter $nf, array $args )
+ * Format a message.
+ */
+PHP_FUNCTION( msgfmt_format )
+{
+ zval *args;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+ &object, MessageFormatter_ce_ptr, &args ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ msgfmt_do_format(mfo, args, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto mixed MessageFormatter::formatMessage( string $locale, string $pattern, array $args )
+ * Format a message. }}} */
+/* {{{ proto mixed msgfmt_format_message( string $locale, string $pattern, array $args )
+ * Format a message.
+ */
+PHP_FUNCTION( msgfmt_format_message )
+{
+ zval *args;
+ UChar *spattern = NULL;
+ int spattern_len = 0;
+ char *pattern = NULL;
+ int pattern_len = 0;
+ char *slocale = NULL;
+ int slocale_len = 0;
+ MessageFormatter_object mf = {0};
+ MessageFormatter_object *mfo = &mf;
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "ssa",
+ &slocale, &slocale_len, &pattern, &pattern_len, &args ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ msgformat_data_init(&mfo->mf_data TSRMLS_CC);
+
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE(INTL_DATA_ERROR_CODE((mfo))) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format_message: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(slocale_len == 0) {
+ slocale = INTL_G(default_locale);
+ }
+
+ if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_format_message: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ // Create an ICU message formatter.
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, slocale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ if(spattern && spattern_len) {
+ efree(spattern);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Creating message formatter failed");
+
+ msgfmt_do_format(mfo, args, return_value TSRMLS_CC);
+
+ // drop the temporary formatter
+ msgformat_data_free(&mfo->mf_data 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/msgformat/msgformat_format.h b/ext/intl/msgformat/msgformat_format.h
new file mode 100755
index 0000000000..b74deab8ff
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_format.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_FORMAT_H
+#define MSG_FORMAT_FORMAT_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_format );
+PHP_FUNCTION( msgfmt_format_message );
+
+#endif // MSG_FORMAT_FORMAT_H
diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp
new file mode 100755
index 0000000000..8b7c8e6f87
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_helpers.cpp
@@ -0,0 +1,211 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include <unicode/msgfmt.h>
+
+extern "C" {
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_format.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+}
+
+U_NAMESPACE_BEGIN
+/**
+ * This class isolates our access to private internal methods of
+ * MessageFormat. It is never instantiated; it exists only for C++
+ * access management.
+ */
+class MessageFormatAdapter {
+public:
+ static const Formattable::Type* getArgTypeList(const MessageFormat& m,
+ int32_t& count);
+};
+const Formattable::Type*
+MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
+ int32_t& count) {
+ return m.getArgTypeList(count);
+}
+U_NAMESPACE_END
+
+U_CFUNC int32_t umsg_format_arg_count(UMessageFormat *fmt)
+{
+ int32_t fmt_count = 0;
+ MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
+ return fmt_count;
+}
+
+U_CFUNC void umsg_format_helper(UMessageFormat *fmt, int arg_count, zval **args, UChar **formatted, int *formatted_len, UErrorCode *status TSRMLS_DC)
+{
+ int fmt_count = 0;
+ const Formattable::Type* argTypes =
+ MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
+ Formattable* fargs = new Formattable[fmt_count ? fmt_count : 1];
+
+ for(int32_t i = 0; i < fmt_count; ++i) {
+ UChar *stringVal = NULL;
+ int stringLen = 0;
+ int64_t tInt64 = 0;
+
+ switch(argTypes[i]) {
+ case Formattable::kDate:
+ convert_to_long_ex(&args[i]);
+ fargs[i].setDate(U_MILLIS_PER_SECOND * (double)Z_LVAL_P(args[i]));
+ break;
+
+ case Formattable::kDouble:
+ convert_to_double_ex(&args[i]);
+ fargs[i].setDouble(Z_DVAL_P(args[i]));
+ break;
+
+ case Formattable::kLong:
+ convert_to_long_ex(&args[i]);
+ fargs[i].setLong(Z_LVAL_P(args[i]));
+ break;
+
+ case Formattable::kInt64:
+ if(Z_TYPE_P(args[i]) == IS_DOUBLE) {
+ tInt64 = (int64_t)Z_DVAL_P(args[i]);
+ } else if(Z_TYPE_P(args[i]) == IS_LONG) {
+ tInt64 = (int64_t)Z_LVAL_P(args[i]);
+ } else {
+ SEPARATE_ZVAL_IF_NOT_REF(&args[i]);
+ convert_scalar_to_number( args[i] TSRMLS_CC );
+ tInt64 = (Z_TYPE_P(args[i]) == IS_DOUBLE)?(int64_t)Z_DVAL_P(args[i]):Z_LVAL_P(args[i]);
+ }
+ fargs[i].setInt64(tInt64);
+ break;
+
+ case Formattable::kString:
+ convert_to_string_ex(&args[i]);
+ intl_convert_utf8_to_utf16(&stringVal, &stringLen, Z_STRVAL_P(args[i]), Z_STRLEN_P(args[i]), status);
+ if(U_FAILURE(*status)){
+ delete[] fargs;
+ return;
+ }
+ fargs[i].setString(stringVal);
+ efree(stringVal);
+ break;
+
+ case Formattable::kArray:
+ case Formattable::kObject:
+ *status = U_UNSUPPORTED_ERROR;
+ delete[] fargs;
+ return;
+ }
+ }
+
+ UnicodeString resultStr;
+ FieldPosition fieldPosition(0);
+
+ /* format the message */
+ ((const MessageFormat*)fmt)->format(fargs, fmt_count, resultStr, fieldPosition, *status);
+
+ delete[] fargs;
+
+ if(U_FAILURE(*status)){
+ return;
+ }
+
+ *formatted_len = resultStr.length();
+ *formatted = eumalloc(*formatted_len+1);
+ resultStr.extract(*formatted, *formatted_len+1, *status);
+}
+
+#define cleanup_zvals() for(int j=i;j>=0;j--) { zval_ptr_dtor((*args)+i); }
+
+U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval ***args, UChar *source, int source_len, UErrorCode *status)
+{
+ UnicodeString srcString(source, source_len);
+ Formattable *fargs = ((const MessageFormat*)fmt)->parse(srcString, *count, *status);
+
+ if(U_FAILURE(*status)) {
+ return;
+ }
+
+ *args = (zval **)safe_emalloc(*count, sizeof(zval *), 0);
+
+ // assign formattables to varargs
+ for(int32_t i = 0; i < *count; i++) {
+ int64_t aInt64;
+ double aDate;
+ UnicodeString temp;
+ char *stmp;
+ int stmp_len;
+
+ ALLOC_INIT_ZVAL((*args)[i]);
+
+ switch(fargs[i].getType()) {
+ case Formattable::kDate:
+ aDate = ((double)fargs[i].getDate())/U_MILLIS_PER_SECOND;
+ if(aDate > LONG_MAX || aDate < -LONG_MAX) {
+ ZVAL_DOUBLE((*args)[i], aDate<0?ceil(aDate):floor(aDate));
+ } else {
+ ZVAL_LONG((*args)[i], (long)aDate);
+ }
+ break;
+
+ case Formattable::kDouble:
+ ZVAL_DOUBLE((*args)[i], (double)fargs[i].getDouble());
+ break;
+
+ case Formattable::kLong:
+ ZVAL_LONG((*args)[i], fargs[i].getLong());
+ break;
+
+ case Formattable::kInt64:
+ aInt64 = fargs[i].getInt64();
+ if(aInt64 > LONG_MAX || aInt64 < -LONG_MAX) {
+ ZVAL_DOUBLE((*args)[i], (double)aInt64);
+ } else {
+ ZVAL_LONG((*args)[i], (long)aInt64);
+ }
+ break;
+
+ case Formattable::kString:
+ fargs[i].getString(temp);
+ intl_convert_utf16_to_utf8(&stmp, &stmp_len, temp.getBuffer(), temp.length(), status);
+ if(U_FAILURE(*status)) {
+ cleanup_zvals();
+ return;
+ }
+ ZVAL_STRINGL((*args)[i], stmp, stmp_len, 0);
+ break;
+
+ case Formattable::kObject:
+ case Formattable::kArray:
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ cleanup_zvals();
+ break;
+ }
+ }
+ delete[] fargs;
+}
+
+/*
+ * 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/msgformat/msgformat_helpers.h b/ext/intl/msgformat/msgformat_helpers.h
new file mode 100755
index 0000000000..30c7e3930f
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_helpers.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_HELPERS_H
+#define MSG_FORMAT_HELPERS_H
+
+int32_t umsg_format_arg_count(UMessageFormat *fmt);
+void umsg_format_helper(UMessageFormat *fmt, int arg_count, zval **args,
+ UChar **formatted, int *formatted_len, UErrorCode *status TSRMLS_DC);
+void umsg_parse_helper(UMessageFormat *fmt, int *count, zval ***args,
+ UChar *source, int source_len, UErrorCode *status);
+#endif // MSG_FORMAT_HELPERS_H
diff --git a/ext/intl/msgformat/msgformat_parse.c b/ext/intl/msgformat/msgformat_parse.c
new file mode 100755
index 0000000000..decd737a6c
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_parse.c
@@ -0,0 +1,157 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_parse.h"
+#include "msgformat_data.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+
+/* {{{ */
+static void msgfmt_do_parse(MessageFormatter_object *mfo, char *source, int src_len, zval *return_value TSRMLS_DC)
+{
+ zval **fargs;
+ int count = 0;
+ int i;
+ UChar *usource = NULL;
+ int usrc_len = 0;
+
+ intl_convert_utf8_to_utf16(&usource, &usrc_len, source, src_len, &INTL_DATA_ERROR_CODE(mfo));
+ INTL_METHOD_CHECK_STATUS(mfo, "Converting parse string failed");
+
+ umsg_parse_helper(MSG_FORMAT_OBJECT(mfo), &count, &fargs, usource, usrc_len, &INTL_DATA_ERROR_CODE(mfo));
+ efree(usource);
+ INTL_METHOD_CHECK_STATUS(mfo, "Parsing failed");
+
+ array_init(return_value);
+ for(i=0;i<count;i++) {
+ add_next_index_zval(return_value, fargs[i]);
+ }
+ efree(fargs);
+}
+/* }}} */
+
+/* {{{ proto array MessageFormatter::parse( string $source )
+ * Parse a message }}} */
+/* {{{ proto array msgfmt_parse( MessageFormatter $nf, string $source )
+ * Parse a message.
+ */
+PHP_FUNCTION( msgfmt_parse )
+{
+ char *source;
+ int source_len;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, MessageFormatter_ce_ptr, &source, &source_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ // Fetch the object.
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ msgfmt_do_parse(mfo, source, source_len, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto array MessageFormatter::formatMessage( string $locale, string $pattern, string $source )
+ * Parse a message. }}} */
+/* {{{ proto array numfmt_parse_message( string $locale, string $pattern, string $source )
+ * Parse a message.
+ */
+PHP_FUNCTION( msgfmt_parse_message )
+{
+ UChar *spattern = NULL;
+ int spattern_len = 0;
+ char *pattern = NULL;
+ int pattern_len = 0;
+ char *slocale = NULL;
+ int slocale_len = 0;
+ char *source = NULL;
+ int src_len = 0;
+ MessageFormatter_object mf = {0};
+ MessageFormatter_object *mfo = &mf;
+
+ // Parse parameters.
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sss",
+ &slocale, &slocale_len, &pattern, &pattern_len, &source, &src_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ msgformat_data_init(&mfo->mf_data TSRMLS_CC);
+
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE(INTL_DATA_ERROR_CODE((mfo))) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse_message: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(slocale_len == 0) {
+ slocale = INTL_G(default_locale);
+ }
+
+ if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_parse_message: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ // Create an ICU message formatter.
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, slocale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ if(spattern && spattern_len) {
+ efree(spattern);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Creating message formatter failed");
+
+ msgfmt_do_parse(mfo, source, src_len, return_value TSRMLS_CC);
+
+ // drop the temporary formatter
+ msgformat_data_free(&mfo->mf_data 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/msgformat/msgformat_parse.h b/ext/intl/msgformat/msgformat_parse.h
new file mode 100755
index 0000000000..a937235839
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_parse.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_PARSE_H
+#define MSG_FORMAT_PARSE_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_parse );
+PHP_FUNCTION( msgfmt_parse_message );
+
+#endif // MSG_FORMAT_PARSE_H
diff --git a/ext/intl/normalizer/normalizer.c b/ext/intl/normalizer/normalizer.c
new file mode 100755
index 0000000000..f0694133b9
--- /dev/null
+++ b/ext/intl/normalizer/normalizer.c
@@ -0,0 +1,68 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "normalizer_class.h"
+#include "normalizer.h"
+
+#include <unicode/utypes.h>
+#include <unicode/unorm.h>
+#include <unicode/ustring.h>
+
+/* {{{ normalizer_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void normalizer_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Normalizer_ce_ptr )
+ {
+ zend_error( E_ERROR, "Normalizer class not defined" );
+ return;
+ }
+
+ #define NORMALIZER_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define NORMALIZER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Normalizer_ce_ptr, ZEND_STRS( #x ) - 1, NORMALIZER_##x TSRMLS_CC );
+ #define NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( Normalizer_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ // Normalization form constants
+ NORMALIZER_EXPOSE_CLASS_CONST( NONE );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_D );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFD );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_KD );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFKD );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_C );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFC );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_KC );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFKC );
+
+ #undef NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_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/normalizer/normalizer.h b/ext/intl/normalizer/normalizer.h
new file mode 100755
index 0000000000..eca9abe05a
--- /dev/null
+++ b/ext/intl/normalizer/normalizer.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef NORMALIZER_NORMALIZER_H
+#define NORMALIZER_NORMALIZER_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+#include <unicode/unorm.h>
+
+#define NORMALIZER_NONE UNORM_NONE
+#define NORMALIZER_FORM_D UNORM_NFD
+#define NORMALIZER_NFD UNORM_NFD
+#define NORMALIZER_FORM_KD UNORM_NFKD
+#define NORMALIZER_NFKD UNORM_NFKD
+#define NORMALIZER_FORM_C UNORM_NFC
+#define NORMALIZER_NFC UNORM_NFC
+#define NORMALIZER_FORM_KC UNORM_NFKC
+#define NORMALIZER_NFKC UNORM_NFKC
+#define NORMALIZER_DEFAULT UNORM_DEFAULT
+
+void normalizer_register_constants( INIT_FUNC_ARGS );
+
+#endif // NORMALIZER_NORMALIZER_H
diff --git a/ext/intl/normalizer/normalizer_class.c b/ext/intl/normalizer/normalizer_class.c
new file mode 100755
index 0000000000..44844e8486
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_class.c
@@ -0,0 +1,82 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "normalizer_class.h"
+#include "php_intl.h"
+#include "normalizer_normalize.h"
+#include "intl_error.h"
+
+#include <unicode/unorm.h>
+
+zend_class_entry *Normalizer_ce_ptr = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// 'Normalizer' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ Normalizer methods arguments info */
+
+static
+ZEND_BEGIN_ARG_INFO_EX( normalizer_3_args, 0, 0, 3 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ Normalizer_class_functions
+ * Every 'Normalizer' class method has an entry in this table
+ */
+
+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 )
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ normalizer_register_Normalizer_class
+ * Initialize 'Normalizer' class
+ */
+void normalizer_register_Normalizer_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ // Create and register 'Normalizer' class.
+ INIT_CLASS_ENTRY( ce, "Normalizer", Normalizer_class_functions );
+ ce.create_object = NULL;
+ Normalizer_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ // Declare 'Normalizer' class properties.
+ if( !Normalizer_ce_ptr )
+ {
+ zend_error( E_ERROR,
+ "Normalizer: attempt to create properties "
+ "on a non-registered class." );
+ return;
+ }
+}
+/* }}} */
+
+/*
+ * 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/normalizer/normalizer_class.h b/ext/intl/normalizer/normalizer_class.h
new file mode 100755
index 0000000000..1d21b7a417
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_class.h
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef NORMALIZER_CLASS_H
+#define NORMALIZER_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+
+#include <unicode/unorm.h>
+
+typedef struct {
+ zend_object zo;
+
+ // error value not used currently
+ intl_error err;
+
+} Normalizer_object;
+
+#define NORMALIZER_ERROR(co) (co)->err
+#define NORMALIZER_ERROR_P(co) &(NORMALIZER_ERROR(co))
+
+#define NORMALIZER_ERROR_CODE(co) INTL_ERROR_CODE(NORMALIZER_ERROR(co))
+#define NORMALIZER_ERROR_CODE_P(co) &(INTL_ERROR_CODE(NORMALIZER_ERROR(co)))
+
+void normalizer_register_Normalizer_class( TSRMLS_D );
+void normalizer_object_init( Normalizer_object* co TSRMLS_DC );
+void normalizer_object_destroy( Normalizer_object* co TSRMLS_DC );
+
+extern zend_class_entry *Normalizer_ce_ptr;
+
+/* Auxiliary macros */
+
+#define NORMALIZER_METHOD_INIT_VARS \
+ intl_error_reset( NULL TSRMLS_CC ); \
+
+#endif // #ifndef NORMALIZER_CLASS_H
diff --git a/ext/intl/normalizer/normalizer_normalize.c b/ext/intl/normalizer/normalizer_normalize.c
new file mode 100755
index 0000000000..265625e014
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_normalize.c
@@ -0,0 +1,256 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "unicode/unorm.h"
+#include "normalizer.h"
+#include "normalizer_class.h"
+#include "normalizer_normalize.h"
+#include "intl_convert.h"
+
+/* {{{ proto string Normalizer::normalize( string $input [, string $form = FORM_C] )
+ * Normalize a string. }}} */
+/* {{{ proto string normalizer_normalize( string $input [, string $form = FORM_C] )
+ * Normalize a string.
+ */
+PHP_FUNCTION( normalizer_normalize )
+{
+ char* input = NULL;
+ // form is optional, defaults to FORM_C
+ long form = NORMALIZER_DEFAULT;
+ int input_len = 0;
+
+ UChar* uinput = NULL;
+ int uinput_len = 0;
+ int expansion_factor = 1;
+ UErrorCode status = U_ZERO_ERROR;
+
+ UChar* uret_buf = NULL;
+ int uret_len = 0;
+
+ char* ret_buf = NULL;
+ int32_t ret_len = 0;
+
+ int32_t size_needed;
+
+ NORMALIZER_METHOD_INIT_VARS
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "s|l",
+ &input, &input_len, &form ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_normalize: unable to parse input params", 1 TSRMLS_CC );
+
+ RETURN_NULL();
+ }
+
+ expansion_factor = 1;
+
+ switch(form) {
+ case NORMALIZER_NONE:
+ break;
+ case NORMALIZER_FORM_D:
+ expansion_factor = 3;
+ break;
+ case NORMALIZER_FORM_KD:
+ expansion_factor = 3;
+ break;
+ case NORMALIZER_FORM_C:
+ case NORMALIZER_FORM_KC:
+ break;
+ default:
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_normalize: illegal normalization form", 1 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ /*
+ * Normalize string (converting it to UTF-16 first).
+ */
+
+ // First convert the string to UTF-16.
+ intl_convert_utf8_to_utf16(&uinput, &uinput_len, input, input_len, &status );
+
+ if( U_FAILURE( status ) )
+ {
+ // Set global error code.
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ // Set error messages.
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC );
+ efree( uinput );
+ RETURN_NULL();
+ }
+
+
+ // Allocate memory for the destination buffer for normalization
+ uret_len = uinput_len * expansion_factor;
+ uret_buf = eumalloc( uret_len + 1 );
+
+ // 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.
+ // (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).
+ if( U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING ) {
+ efree( uret_buf );
+ efree( uinput );
+ RETURN_NULL();
+ }
+
+ if ( size_needed > uret_len ) {
+ // realloc does not seem to work properly - memory is corrupted
+ // uret_buf = eurealloc(uret_buf, size_needed + 1);
+ efree( uret_buf );
+ uret_buf = eumalloc( size_needed + 1 );
+ uret_len = size_needed;
+
+ status = U_ZERO_ERROR;
+
+ // 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.
+ if( U_FAILURE(status) ) {
+ // Set error messages.
+ intl_error_set_custom_msg( NULL,"Error normalizing string", 1 TSRMLS_CC );
+ efree( uret_buf );
+ efree( uinput );
+ RETURN_NULL();
+ }
+ }
+
+ efree( uinput );
+
+ // the buffer we actually used
+ uret_len = size_needed;
+
+ // Convert normalized string from UTF-16 to UTF-8.
+ intl_convert_utf16_to_utf8( &ret_buf, &ret_len, uret_buf, uret_len, &status );
+ efree( uret_buf );
+ if( U_FAILURE( status ) )
+ {
+ intl_error_set( NULL, status,
+ "normalizer_normalize: error converting normalized text UTF-8", 1 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ // Return it.
+ RETVAL_STRINGL( ret_buf, ret_len, FALSE );
+}
+/* }}} */
+
+/* {{{ proto bool Normalizer::isNormalized( string $input [, string $form = FORM_C] )
+ * Test if a string is in a given normalization form. }}} */
+/* {{{ proto bool normalizer_is_normalize( string $input [, string $form = FORM_C] )
+ * Test if a string is in a given normalization form.
+ */
+PHP_FUNCTION( normalizer_is_normalized )
+{
+ char* input = NULL;
+ // form is optional, defaults to FORM_C
+ long form = NORMALIZER_DEFAULT;
+ int input_len = 0;
+
+ UChar* uinput = NULL;
+ int uinput_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ UBool uret = FALSE;
+
+ NORMALIZER_METHOD_INIT_VARS
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ // Parse parameters.
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "s|l",
+ &input, &input_len, &form) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_is_normalized: unable to parse input params", 1 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ switch(form) {
+ // case NORMALIZER_NONE: not allowed - doesn't make sense
+
+ case NORMALIZER_FORM_D:
+ case NORMALIZER_FORM_KD:
+ case NORMALIZER_FORM_C:
+ case NORMALIZER_FORM_KC:
+ break;
+ default:
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_normalize: illegal normalization form", 1 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+
+ /*
+ * Test normalization of string (converting it to UTF-16 first).
+ */
+
+ // First convert the string to UTF-16.
+ intl_convert_utf8_to_utf16(&uinput, &uinput_len, input, input_len, &status );
+
+ if( U_FAILURE( status ) )
+ {
+ // Set global error code.
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ // Set error messages.
+ intl_error_set_custom_msg( NULL, "Error converting string to UTF-16.", 1 TSRMLS_CC );
+ efree( uinput );
+ RETURN_FALSE;
+ }
+
+
+ // test string
+ uret = unorm_isNormalizedWithOptions( uinput, uinput_len, form, (int32_t) 0 /* options */, &status);
+
+ efree( uinput );
+
+ // Bail out if an unexpected error occured.
+ if( U_FAILURE(status) ) {
+ // Set error messages.
+ intl_error_set_custom_msg( NULL,"Error testing if string is the given normalization form.", 1 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ if ( uret )
+ RETURN_TRUE;
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/*
+ * 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/normalizer/normalizer_normalize.h b/ext/intl/normalizer/normalizer_normalize.h
new file mode 100755
index 0000000000..41c31f7949
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_normalize.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef NORMALIZER_NORMALIZE_H
+#define NORMALIZER_NORMALIZE_H
+
+#include <php.h>
+
+PHP_FUNCTION( normalizer_normalize );
+PHP_FUNCTION( normalizer_is_normalized );
+
+#endif // NORMALIZER_NORMALIZE_H
diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c
new file mode 100755
index 0000000000..2b68734dc0
--- /dev/null
+++ b/ext/intl/php_intl.c
@@ -0,0 +1,490 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "php_intl.h"
+#include "intl_error.h"
+#include "collator/collator_class.h"
+#include "collator/collator.h"
+#include "collator/collator_attr.h"
+#include "collator/collator_compare.h"
+#include "collator/collator_sort.h"
+#include "collator/collator_convert.h"
+#include "collator/collator_locale.h"
+#include "collator/collator_create.h"
+#include "collator/collator_error.h"
+
+#include "formatter/formatter.h"
+#include "formatter/formatter_class.h"
+#include "formatter/formatter_attr.h"
+#include "formatter/formatter_format.h"
+#include "formatter/formatter_main.h"
+#include "formatter/formatter_parse.h"
+
+#include "msgformat/msgformat.h"
+#include "msgformat/msgformat_class.h"
+#include "msgformat/msgformat_attr.h"
+#include "msgformat/msgformat_format.h"
+#include "msgformat/msgformat_parse.h"
+
+#include "normalizer/normalizer.h"
+#include "normalizer/normalizer_class.h"
+#include "normalizer/normalizer_normalize.h"
+
+#include "locale/locale.h"
+#include "locale/locale_class.h"
+#include "locale/locale_methods.h"
+
+#include "dateformat/dateformat.h"
+#include "dateformat/dateformat_class.h"
+#include "dateformat/dateformat_attr.h"
+#include "dateformat/dateformat_format.h"
+#include "dateformat/dateformat_parse.h"
+#include "dateformat/dateformat_data.h"
+
+#include "msgformat/msgformat.h"
+#include "common/common_error.h"
+
+#include <unicode/uloc.h>
+#include <ext/standard/info.h>
+
+#include "php_ini.h"
+#define INTL_MODULE_VERSION PHP_INTL_VERSION
+
+/*
+ * locale_get_default has a conflict since ICU also has
+ * a function with the same name
+ * in fact ICU appends the version no. to it also
+ * Hence the following undef for ICU version
+ * Same true for the locale_set_default function
+*/
+#undef locale_get_default
+#undef locale_set_default
+
+ZEND_DECLARE_MODULE_GLOBALS( intl )
+
+/* {{{ Arguments info */
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_static_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_static_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_static_2_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_0_args, 0, 0, 1 )
+ ZEND_ARG_OBJ_INFO( 0, object, Collator, 0 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_1_arg, 0, 0, 2 )
+ ZEND_ARG_OBJ_INFO( 0, object, Collator, 0 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_2_args, 0, 0, 3 )
+ ZEND_ARG_OBJ_INFO( 0, object, Collator, 0 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( collator_sort_args, 0, 0, 2 )
+ ZEND_ARG_OBJ_INFO( 0, object, Collator, 0 )
+ ZEND_ARG_ARRAY_INFO( 1, arr, 0 )
+ ZEND_ARG_INFO( 0, sort_flags )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( numfmt_parse_arginfo, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, formatter )
+ ZEND_ARG_INFO( 0, string )
+ ZEND_ARG_INFO( 0, type )
+ ZEND_ARG_INFO( 1, position )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( numfmt_parse_currency_arginfo, 0, 0, 3 )
+ ZEND_ARG_INFO( 0, formatter )
+ ZEND_ARG_INFO( 0, string )
+ ZEND_ARG_INFO( 1, currency )
+ ZEND_ARG_INFO( 1, position )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_2_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ZEND_END_ARG_INFO()
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_3_args, 0, 0, 3 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_4_args, 0, 0, 4 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ ZEND_ARG_INFO( 0, arg4 )
+ZEND_END_ARG_INFO()
+
+#define intl_0_args collator_static_0_args
+#define intl_1_arg collator_static_1_arg
+
+static
+ZEND_BEGIN_ARG_INFO_EX( normalizer_args, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, input )
+ ZEND_ARG_INFO( 0, form )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( grapheme_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, string )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( grapheme_search_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, haystack )
+ ZEND_ARG_INFO( 0, needle )
+ ZEND_ARG_INFO( 0, offset )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( grapheme_substr_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, string )
+ ZEND_ARG_INFO( 0, start )
+ ZEND_ARG_INFO( 0, length )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( grapheme_strstr_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, haystack )
+ ZEND_ARG_INFO( 0, needle )
+ ZEND_ARG_INFO( 0, before_needle )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( grapheme_extract_args, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, arg1 )
+ ZEND_ARG_INFO( 0, arg2 )
+ ZEND_ARG_INFO( 0, arg3 )
+ ZEND_ARG_INFO( 0, arg4 )
+ ZEND_ARG_INFO( 1, arg5 ) /* 1 = pass by reference */
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ intl_functions
+ *
+ * Every user visible function must have an entry in intl_functions[].
+ */
+zend_function_entry intl_functions[] = {
+
+ // collator functions
+ PHP_FE( collator_create, collator_static_1_arg )
+ PHP_FE( collator_compare, collator_2_args )
+ PHP_FE( collator_get_attribute, collator_1_arg )
+ PHP_FE( collator_set_attribute, collator_2_args )
+ PHP_FE( collator_get_strength, collator_0_args )
+ PHP_FE( collator_set_strength, collator_1_arg )
+ PHP_FE( collator_sort, collator_sort_args )
+ PHP_FE( collator_sort_with_sort_keys, collator_sort_args )
+ PHP_FE( collator_asort, collator_sort_args )
+ PHP_FE( collator_get_locale, collator_1_arg )
+ PHP_FE( collator_get_error_code, collator_0_args )
+ PHP_FE( collator_get_error_message, collator_0_args )
+
+ // formatter functions
+ PHP_FE( numfmt_create, NULL )
+ PHP_FE( numfmt_format, NULL )
+ PHP_FE( numfmt_parse, numfmt_parse_arginfo )
+ PHP_FE( numfmt_format_currency, NULL )
+ PHP_FE( numfmt_parse_currency, numfmt_parse_currency_arginfo )
+ PHP_FE( numfmt_set_attribute, NULL )
+ PHP_FE( numfmt_get_attribute, NULL )
+ PHP_FE( numfmt_set_text_attribute, NULL )
+ PHP_FE( numfmt_get_text_attribute, NULL )
+ PHP_FE( numfmt_set_symbol, NULL )
+ PHP_FE( numfmt_get_symbol, NULL )
+ PHP_FE( numfmt_set_pattern, NULL )
+ PHP_FE( numfmt_get_pattern, NULL )
+ PHP_FE( numfmt_get_locale, NULL )
+ PHP_FE( numfmt_get_error_code, NULL )
+ PHP_FE( numfmt_get_error_message, NULL )
+
+ // normalizer functions
+ PHP_FE( normalizer_normalize, normalizer_args )
+ PHP_FE( normalizer_is_normalized, normalizer_args )
+
+ //Locale functions
+ PHP_NAMED_FE( locale_get_default, zif_locale_get_default, locale_0_args )
+ PHP_NAMED_FE( locale_set_default, zif_locale_set_default, locale_1_arg )
+ PHP_FE( locale_get_primary_language, locale_1_arg )
+ PHP_FE( locale_get_script, locale_1_arg )
+ PHP_FE( locale_get_region, locale_1_arg )
+ PHP_FE( locale_get_keywords, locale_1_arg )
+ PHP_FE( locale_get_display_script, locale_2_args )
+ PHP_FE( locale_get_display_region, locale_2_args )
+ PHP_FE( locale_get_display_name, locale_2_args )
+ PHP_FE( locale_get_display_language, locale_2_args)
+ PHP_FE( locale_get_display_variant, locale_2_args )
+ PHP_FE( locale_compose, locale_1_arg )
+ PHP_FE( locale_parse, locale_1_arg )
+ PHP_FE( locale_get_all_variants, locale_1_arg )
+ PHP_FE( locale_filter_matches, locale_3_args )
+ PHP_FE( locale_canonicalize, locale_1_arg )
+ PHP_FE( locale_lookup, locale_4_args )
+
+ // MessageFormatter functions
+ PHP_FE( msgfmt_create, NULL )
+ PHP_FE( msgfmt_format, NULL )
+ PHP_FE( msgfmt_format_message, NULL )
+ PHP_FE( msgfmt_parse, NULL )
+ PHP_FE( msgfmt_parse_message, NULL )
+ PHP_FE( msgfmt_set_pattern, NULL )
+ PHP_FE( msgfmt_get_pattern, NULL )
+ PHP_FE( msgfmt_get_locale, NULL )
+ PHP_FE( msgfmt_get_error_code, NULL )
+ PHP_FE( msgfmt_get_error_message, NULL )
+
+ // IntlDateFormatter functions
+ PHP_FE( datefmt_create, NULL )
+ PHP_FE( datefmt_get_datetype, NULL )
+ PHP_FE( datefmt_get_timetype, NULL )
+ PHP_FE( datefmt_get_calendar, NULL )
+ PHP_FE( datefmt_set_calendar, NULL )
+ PHP_FE( datefmt_get_locale, NULL )
+ PHP_FE( datefmt_get_timezone_id, NULL )
+ PHP_FE( datefmt_set_timezone_id, NULL )
+ PHP_FE( datefmt_get_pattern, NULL )
+ PHP_FE( datefmt_set_pattern, NULL )
+ PHP_FE( datefmt_is_lenient, NULL )
+ PHP_FE( datefmt_set_lenient, NULL )
+ PHP_FE( datefmt_format, NULL )
+ PHP_FE( datefmt_parse, NULL )
+ PHP_FE( datefmt_localtime , NULL )
+ PHP_FE( datefmt_get_error_code, NULL )
+ PHP_FE( datefmt_get_error_message, NULL )
+
+ // grapheme functions
+ PHP_FE( grapheme_strlen, grapheme_1_arg )
+ PHP_FE( grapheme_strpos, grapheme_search_args )
+ PHP_FE( grapheme_stripos, grapheme_search_args )
+ PHP_FE( grapheme_strrpos, grapheme_search_args )
+ PHP_FE( grapheme_strripos, grapheme_search_args )
+ PHP_FE( grapheme_substr, grapheme_substr_args )
+ PHP_FE( grapheme_strstr, grapheme_strstr_args )
+ PHP_FE( grapheme_stristr, grapheme_strstr_args )
+ PHP_FE( grapheme_extract, grapheme_extract_args )
+
+ // common functions
+ PHP_FE( intl_get_error_code, intl_0_args )
+ PHP_FE( intl_get_error_message, intl_0_args )
+ PHP_FE( intl_is_failure, intl_1_arg )
+ PHP_FE( intl_error_name, intl_1_arg )
+
+ { NULL, NULL, NULL }
+};
+/* }}} */
+
+
+/* {{{ INI Settings */
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY(LOCALE_INI_NAME, NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_locale, zend_intl_globals, intl_globals)
+
+PHP_INI_END()
+/* }}} */
+
+
+static PHP_GINIT_FUNCTION(intl);
+
+/* {{{ intl_module_entry */
+zend_module_entry intl_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "intl",
+ intl_functions,
+ PHP_MINIT( intl ),
+ PHP_MSHUTDOWN( intl ),
+ PHP_RINIT( intl ),
+ PHP_RSHUTDOWN( intl ),
+ PHP_MINFO( intl ),
+ INTL_MODULE_VERSION,
+ PHP_MODULE_GLOBALS(intl), /* globals descriptor */
+ PHP_GINIT(intl), /* globals ctor */
+ NULL, /* globals dtor */
+ NULL, /* post deactivate */
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+#ifdef COMPILE_DL_INTL
+ZEND_GET_MODULE( intl )
+#endif
+
+/* {{{ intl_init_globals */
+static PHP_GINIT_FUNCTION(intl)
+{
+ memset( intl_globals, 0, sizeof(zend_intl_globals) );
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION( intl )
+{
+ //For the default locale php.ini setting
+ REGISTER_INI_ENTRIES();
+
+ REGISTER_LONG_CONSTANT("INTL_MAX_LOCALE_LEN", INTL_MAX_LOCALE_LEN, CONST_CS);
+
+ // Register 'Collator' PHP class
+ collator_register_Collator_class( TSRMLS_C );
+
+ // Expose Collator constants to PHP scripts
+ collator_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Register 'NumberFormatter' PHP class
+ formatter_register_class( TSRMLS_C );
+
+ // Expose NumberFormatter constants to PHP scripts
+ formatter_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Register 'Normalizer' PHP class
+ normalizer_register_Normalizer_class( TSRMLS_C );
+
+ // Expose Normalizer constants to PHP scripts
+ normalizer_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Register 'Locale' PHP class
+ locale_register_Locale_class( TSRMLS_C );
+
+ // Expose Locale constants to PHP scripts
+ locale_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ msgformat_register_class(TSRMLS_C);
+
+ grapheme_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Register 'DateFormat' PHP class
+ dateformat_register_IntlDateFormatter_class( TSRMLS_C );
+
+ // Expose DateFormat constants to PHP scripts
+ dateformat_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Expose ICU error codes to PHP scripts.
+ intl_expose_icu_error_codes( INIT_FUNC_ARGS_PASSTHRU );
+
+ // Global error handling.
+ intl_error_init( NULL TSRMLS_CC );
+
+ //Set the default_locale value
+ if( INTL_G(default_locale) == NULL ) {
+ INTL_G(default_locale) = pestrdup(uloc_getDefault(), 1) ;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION( intl )
+{
+ //For the default locale php.ini setting
+ UNREGISTER_INI_ENTRIES();
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RINIT_FUNCTION
+ */
+PHP_RINIT_FUNCTION( intl )
+{
+ //Set the default_locale value
+ if( INTL_G(default_locale) == NULL ) {
+ INTL_G(default_locale) = pestrdup(uloc_getDefault(), 1) ;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION( intl )
+{
+ if(INTL_G(current_collator)) {
+ INTL_G(current_collator) = NULL;
+ }
+ if (INTL_G(grapheme_iterator)) {
+ grapheme_close_global_iterator( TSRMLS_C );
+ INTL_G(grapheme_iterator) = NULL;
+ }
+
+ intl_error_reset( NULL TSRMLS_CC);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION( intl )
+{
+ php_info_print_table_start();
+ php_info_print_table_header( 2, "Internationalization support", "enabled" );
+ php_info_print_table_row( 2, "version", INTL_MODULE_VERSION );
+ php_info_print_table_end();
+
+ //For the default locale php.ini setting
+ DISPLAY_INI_ENTRIES() ;
+}
+/* }}} */
+
+/*
+ * 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/php_intl.h b/ext/intl/php_intl.h
new file mode 100755
index 0000000000..92663444e0
--- /dev/null
+++ b/ext/intl/php_intl.h
@@ -0,0 +1,76 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_INTL_H
+#define PHP_INTL_H
+
+#include <php.h>
+
+#include "collator/collator_sort.h"
+#include "grapheme/grapheme.h"
+#include "intl_error.h"
+
+extern zend_module_entry intl_module_entry;
+#define phpext_intl_ptr &intl_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_INTL_API __declspec(dllexport)
+#else
+#define PHP_INTL_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+ZEND_BEGIN_MODULE_GLOBALS(intl)
+ zval* current_collator;
+ char* default_locale;
+ collator_compare_func_t compare_func;
+ UBreakIterator* grapheme_iterator;
+ intl_error g_error;
+ZEND_END_MODULE_GLOBALS(intl)
+
+/* Macro to access request-wide global variables. */
+#ifdef ZTS
+#define INTL_G(v) TSRMG(intl_globals_id, zend_intl_globals *, v)
+#else
+#define INTL_G(v) (intl_globals.v)
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(intl)
+
+PHP_MINIT_FUNCTION(intl);
+PHP_MSHUTDOWN_FUNCTION(intl);
+PHP_RINIT_FUNCTION(intl);
+PHP_RSHUTDOWN_FUNCTION(intl);
+PHP_MINFO_FUNCTION(intl);
+
+#define PHP_INTL_VERSION "1.0.0"
+
+#endif /* PHP_INTL_H */
+
+/*
+ * 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/tests/bug12887.phpt b/ext/intl/tests/bug12887.phpt
new file mode 100755
index 0000000000..e2fc194944
--- /dev/null
+++ b/ext/intl/tests/bug12887.phpt
@@ -0,0 +1,29 @@
+--TEST--
+locale_get_keywords() bug #12887
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function ut_main()
+{
+ $res_str = '';
+ $keywords_arr = ut_loc_get_keywords( 'de_DE@currency=EUR;collation=PHONEBOOK;sort=PHONEBOOK' );
+ if ($keywords_arr) {
+ foreach( $keywords_arr as $key => $value){
+ $res_str .= "$key = $value\n";
+ }
+ }
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+collation = PHONEBOOK
+currency = EUR
+sort = PHONEBOOK
diff --git a/ext/intl/tests/collation_customization.phpt b/ext/intl/tests/collation_customization.phpt
new file mode 100755
index 0000000000..e380380639
--- /dev/null
+++ b/ext/intl/tests/collation_customization.phpt
@@ -0,0 +1,70 @@
+--TEST--
+Collation customization
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Check effects of changing misc collattion options.
+ */
+
+
+function cmp_array( &$coll, $a )
+{
+ $res = '';
+ $prev = null;
+ foreach( $a as $i )
+ {
+ if( is_null( $prev ) )
+ $res .= "$i";
+ else
+ {
+ $eqrc = ut_coll_compare( $coll, $prev, $i );
+ $eq = $eqrc < 0 ? "<" : ( $eqrc > 0 ? ">" : "=" );
+ $res .= " $eq $i";
+ }
+
+ $prev = $i;
+ }
+ $res .= "\n";
+
+ return $res;
+}
+
+function check_alternate_handling( &$coll )
+{
+ $res = '';
+
+ ut_coll_set_strength( $coll, Collator::TERTIARY );
+ ut_coll_set_attribute( $coll, Collator::ALTERNATE_HANDLING, Collator::NON_IGNORABLE );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'Di Silva', 'diSilva', 'U.S.A.', 'USA' ) );
+
+ ut_coll_set_attribute( $coll, Collator::ALTERNATE_HANDLING, Collator::SHIFTED );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'diSilva', 'Di Silva', 'U.S.A.', 'USA' ) );
+
+ ut_coll_set_strength( $coll, Collator::QUATERNARY );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'diSilva', 'Di Silva', 'U.S.A.', 'USA' ) );
+ $res .= "\n";
+
+ return $res;
+}
+
+function ut_main()
+{
+ $coll = ut_coll_create( 'en_US' );
+
+ return
+ check_alternate_handling( $coll );
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+di Silva < Di Silva < diSilva < U.S.A. < USA
+di Silva = diSilva < Di Silva < U.S.A. = USA
+di Silva < diSilva < Di Silva < U.S.A. < USA
diff --git a/ext/intl/tests/collator_asort.phpt b/ext/intl/tests/collator_asort.phpt
new file mode 100755
index 0000000000..a614ddc3f8
--- /dev/null
+++ b/ext/intl/tests/collator_asort.phpt
@@ -0,0 +1,242 @@
+--TEST--
+asort()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort associative arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort various arrays in specified locale.
+ */
+function sort_arrays( $locale, $test_arrays, $sort_flag = Collator::SORT_REGULAR )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $test_arrays as $test_array )
+ {
+ // Try to sort test data.
+ $res_val = ut_coll_asort( $coll, $test_array, $sort_flag );
+
+ // Return output data.
+ $res_dump = "\n" . dump( $test_array ) .
+ "\n Result: " . dump( $res_val );
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+/*
+ * Test main function.
+ */
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'd' => 'y' ,
+ 'c' => 'i' ,
+ 'a' => 'k' ),
+
+ array( 'a' => 'a' ,
+ 'b' => 'aaa',
+ 'c' => 'aa' ),
+
+ array( 'a' => 'a' ,
+ 'aaa'=> 'a' ,
+ 'aa' => 'a' ),
+
+ array( '1' => 'abc',
+ '5' => '!' ,
+ '2' => null ,
+ '7' => '' ),
+
+ array( '1' => '100',
+ '2' => '25' ,
+ '3' => '36' ),
+
+ array( '1' => 5 ,
+ '2' => '30' ,
+ '3' => 2 )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ // Sort an array in SORT_STRING mode using en_US locale.
+ $test_params = array(
+ array( '1' => '100',
+ '2' => '25' ,
+ '3' => '36' ),
+
+ array( '1' => 5 ,
+ '2' => '30' ,
+ '3' => 2 ),
+
+ array( '1' => 'd' ,
+ '2' => '' ,
+ '3' => ' a' ),
+
+ array( '1' => 'y' ,
+ '2' => 'k' ,
+ '3' => 'i' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params, Collator::SORT_STRING );
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'п' => 'у',
+ 'б' => 'в',
+ 'е' => 'а' ),
+
+ array( '1' => 'п',
+ '4' => '',
+ '7' => 'd',
+ '2' => 'пп' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+
+ // Sort an array using Lithuanian locale.
+ $test_params = array(
+ array( 'd' => 'y',
+ 'c' => 'i',
+ 'a' => 'k' )
+ );
+
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str . "\n";
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.162b81ac12878b817fc39063097e45b5:
+array (
+ 'c' => 'i',
+ 'a' => 'k',
+ 'd' => 'y',
+)
+ Result: true
+
+Test 2.93d96e22f692d8a281b0a389f01f8d1e:
+array (
+ 'a' => 'a',
+ 'c' => 'aa',
+ 'b' => 'aaa',
+)
+ Result: true
+
+Test 3.9f25de4482bc7b58de508e278113317c:
+array (
+ 'aa' => 'a',
+ 'aaa' => 'a',
+ 'a' => 'a',
+)
+ Result: true
+
+Test 4.a85a41ea78e45b651080cfd98c0b431d:
+array (
+ 7 => '',
+ 2 => NULL,
+ 5 => '!',
+ 1 => 'abc',
+)
+ Result: true
+
+Test 5.99dc71f405b286e03d489061b36e6900:
+array (
+ 2 => '25',
+ 3 => '36',
+ 1 => '100',
+)
+ Result: true
+
+Test 6.bf5bba243307c9d12934e756ad4be190:
+array (
+ 3 => 2,
+ 1 => 5,
+ 2 => '30',
+)
+ Result: true
+
+Test 7.e4ee7024c61476e9e7a6c28b5e47df6f:
+array (
+ 1 => '100',
+ 2 => '25',
+ 3 => '36',
+)
+ Result: true
+
+Test 8.5fa7033dd43784be0db1474eb48b83c8:
+array (
+ 3 => 2,
+ 2 => '30',
+ 1 => 5,
+)
+ Result: true
+
+Test 9.588cdf4692bc09aa92ffe7e48f9e4579:
+array (
+ 2 => '',
+ 3 => ' a',
+ 1 => 'd',
+)
+ Result: true
+
+Test 10.be02641a47ebcccd23e4183ca3a415f7:
+array (
+ 3 => 'i',
+ 2 => 'k',
+ 1 => 'y',
+)
+ Result: true
+
+Test 11.153d9b11d1e5936afc917a94a4e11f34:
+array (
+ 'е' => 'а',
+ 'б' => 'в',
+ 'п' => 'у',
+)
+ Result: true
+
+Test 12.6eaea3fa21b3b7d006ca7dfa27d4e282:
+array (
+ 4 => '',
+ 7 => 'd',
+ 1 => 'п',
+ 2 => 'пп',
+)
+ Result: true
+
+Test 13.8800d48abb960a59002eef77f1d73ae0:
+array (
+ 'c' => 'i',
+ 'd' => 'y',
+ 'a' => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/collator_compare.phpt b/ext/intl/tests/collator_compare.phpt
new file mode 100755
index 0000000000..f10b5708f4
--- /dev/null
+++ b/ext/intl/tests/collator_compare.phpt
@@ -0,0 +1,134 @@
+--TEST--
+compare()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Compare various string pairs using various locales.
+ */
+
+
+/*
+ * Converts comparison result to a character.
+ */
+function cmp_to_char( $comp_res )
+{
+ switch( $comp_res )
+ {
+ case 0: // UCOL_EQUAL
+ return '=';
+ case 1: // UCOL_GREATER
+ return '>';
+ case -1: // UCOL_LESS
+ return '<';
+ default:
+ return '?';
+ }
+}
+
+/*
+ * Compare string pairs in the given array
+ * using specified locale.
+ */
+function compare_pairs( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $test_array as $test_strings )
+ {
+ list( $str1, $str2 ) = $test_strings;
+
+ // Compare strings.
+ $res_val = cmp_to_char( ut_coll_compare( $coll, $str1, $str2 ) );
+
+ // Concatenate result strings.
+ $res_str .= dump( $str1 ) .
+ ' ' . $res_val . ' ' .
+ dump( $str2 ) . "\n";
+ }
+
+ return $res_str;
+
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Compare strings using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abc' ),
+ array( 'Abc', 'abc' ),
+ array( 'a' , 'abc' ),
+ array( 'a' , '' ),
+ array( '' , '' ),
+ array( 'a' , 'b' ),
+ array( 'ab' , 'b' ),
+ array( 'ab' , 'a' ),
+ array( 123 , 'abc' ),
+ array( 'ac' , null ),
+ array( '.' , '.' ),
+ // Try to compare long strings.
+ array( 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcde',
+ 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdea'),
+ array( null , null )
+ );
+
+ $res_str .= compare_pairs( 'en_US', $test_params );
+
+
+ // Compare strings using ru_RU locale.
+ $test_params = array(
+ array( 'а', 'б' ),
+ array( 'а', 'аа' ),
+ array( 'аб', 'ба' ),
+ array( 'а', ',' ),
+ array( 'а', 'b' ),
+ array( 'а', 'bb' ),
+ array( 'а', 'ab' ),
+ array( 'а', null )
+ );
+
+ $res_str .= compare_pairs( 'ru_RU', $test_params );
+
+
+ // Compare strings using lt_LT locale.
+ $test_params = array(
+ array( 'y', 'k' )
+ );
+
+ $res_str .= compare_pairs( 'lt_LT', $test_params );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+'abc' = 'abc'
+'Abc' > 'abc'
+'a' < 'abc'
+'a' > ''
+'' = ''
+'a' < 'b'
+'ab' < 'b'
+'ab' > 'a'
+123 < 'abc'
+'ac' > NULL
+'.' = '.'
+'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcde' < 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdea'
+NULL = NULL
+'а' < 'б'
+'а' < 'аа'
+'аб' < 'ба'
+'а' > ','
+'а' > 'b'
+'а' > 'bb'
+'а' > 'ab'
+'а' > NULL
+'y' < 'k'
diff --git a/ext/intl/tests/collator_create.phpt b/ext/intl/tests/collator_create.phpt
new file mode 100755
index 0000000000..4a04d3a5d3
--- /dev/null
+++ b/ext/intl/tests/collator_create.phpt
@@ -0,0 +1,81 @@
+--TEST--
+create()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try creating collator with different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $locales = array(
+ 'EN-US-ODESSA',
+ 'UK_UA_ODESSA',
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ '',
+ 'root',
+ 'uk@currency=EURO',
+ '1234567891113151719212325272931333537394143454749515357596163656769717375777981838587899193959799'
+ );
+
+ foreach( $locales as $locale )
+ {
+ // Create Collator with the current locale.
+ $coll = ut_coll_create( $locale );
+ if( !is_object($coll) )
+ {
+ $res_str .= "Error creating collator with '$locale' locale: " .
+ intl_get_error_message() . "\n";
+ continue;
+ }
+
+ // Get the requested, valid and actual locales.
+ $vloc = ut_coll_get_locale( $coll, Locale::VALID_LOCALE );
+ $aloc = ut_coll_get_locale( $coll, Locale::ACTUAL_LOCALE );
+
+ // Show them.
+ $res_str .= "Locale: '$locale'\n" .
+ " ULOC_REQUESTED_LOCALE = '$locale'\n" .
+ " ULOC_VALID_LOCALE = '$vloc'\n" .
+ " ULOC_ACTUAL_LOCALE = '$aloc'\n";
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTF--
+Locale: 'EN-US-ODESSA'
+ ULOC_REQUESTED_LOCALE = 'EN-US-ODESSA'
+ ULOC_VALID_LOCALE = 'en_US'
+ ULOC_ACTUAL_LOCALE = 'en'
+Locale: 'UK_UA_ODESSA'
+ ULOC_REQUESTED_LOCALE = 'UK_UA_ODESSA'
+ ULOC_VALID_LOCALE = 'uk_UA'
+ ULOC_ACTUAL_LOCALE = 'uk'
+Locale: 'uk-ua_CALIFORNIA@currency=;currency=GRN'
+ ULOC_REQUESTED_LOCALE = 'uk-ua_CALIFORNIA@currency=;currency=GRN'
+ ULOC_VALID_LOCALE = 'uk_UA'
+ ULOC_ACTUAL_LOCALE = 'uk'
+Locale: ''
+ ULOC_REQUESTED_LOCALE = ''
+ ULOC_VALID_LOCALE = '%s'
+ ULOC_ACTUAL_LOCALE = '%s'
+Locale: 'root'
+ ULOC_REQUESTED_LOCALE = 'root'
+ ULOC_VALID_LOCALE = 'root'
+ ULOC_ACTUAL_LOCALE = 'root'
+Locale: 'uk@currency=EURO'
+ ULOC_REQUESTED_LOCALE = 'uk@currency=EURO'
+ ULOC_VALID_LOCALE = 'uk'
+ ULOC_ACTUAL_LOCALE = 'uk'
+Error creating collator with '1234567891113151719212325272931333537394143454749515357596163656769717375777981838587899193959799' locale: Locale string too long, should be no longer than 64 characters: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/collator_get_error_code.phpt b/ext/intl/tests/collator_get_error_code.phpt
new file mode 100755
index 0000000000..45a8e710f4
--- /dev/null
+++ b/ext/intl/tests/collator_get_error_code.phpt
@@ -0,0 +1,47 @@
+--TEST--
+get_error_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Retreive error code.
+ */
+
+
+/*
+ * Check if error code equals to expected one.
+ */
+function check_rc( $rc, $expected )
+{
+ return ( $rc === $expected ? "ok" : "failed" ) . "\n";
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'ru_RU' );
+
+ // Try specifying a correct attribute.
+ ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ $status = ut_coll_get_error_code( $coll );
+ $res .= check_rc( $status, U_ZERO_ERROR );
+
+ // Try specifying an incorrect attribute.
+ ut_coll_get_attribute( $coll, 12345 );
+ $status = ut_coll_get_error_code( $coll );
+ $res .= check_rc( $status, U_ILLEGAL_ARGUMENT_ERROR );
+
+ return $res;
+}
+
+# Suppress warning messages.
+error_reporting( E_ERROR );
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+ok
+ok
diff --git a/ext/intl/tests/collator_get_error_message.phpt b/ext/intl/tests/collator_get_error_message.phpt
new file mode 100755
index 0000000000..af4b9c3821
--- /dev/null
+++ b/ext/intl/tests/collator_get_error_message.phpt
@@ -0,0 +1,36 @@
+--TEST--
+get_error_message()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Retreive error message.
+ */
+
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'ru_RU' );
+
+ // Try specifying a correct attribute.
+ ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ $status = ut_coll_get_error_message( $coll );
+ $res .= $status . "\n";
+
+ // Try specifying an incorrect attribute.
+ ut_coll_get_attribute( $coll, 12345 );
+ $status = ut_coll_get_error_message( $coll );
+ $res .= $status . "\n";
+
+ return $res;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+U_ZERO_ERROR
+Error getting attribute value: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/collator_get_locale.phpt b/ext/intl/tests/collator_get_locale.phpt
new file mode 100755
index 0000000000..2fab35eb47
--- /dev/null
+++ b/ext/intl/tests/collator_get_locale.phpt
@@ -0,0 +1,52 @@
+--TEST--
+get_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try to specify valid and invalid locale types when getting locale.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ Locale::VALID_LOCALE,
+ Locale::ACTUAL_LOCALE,
+ 100,
+ -100,
+ -9999999999999,
+ 9999999999999,
+ 1.2,
+ 9999999999999999999999999999999999999999999999
+ );
+
+ $coll = ut_coll_create( 'en_US' );
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $rc = ut_coll_get_locale( $coll, $locale );
+
+ $res_str .= sprintf(
+ "Locale of type %s is %s\n",
+ dump( $locale ),
+ dump( $rc ) );
+ }
+
+ return $res_str . "\n";
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Locale of type 1 is 'en_US'
+Locale of type 0 is 'en'
+Locale of type 100 is false
+Locale of type -100 is false
+Locale of type -9999999999999 is false
+Locale of type 9999999999999 is false
+Locale of type 1.2 is 'en_US'
+Locale of type 1.0E+46 is false
diff --git a/ext/intl/tests/collator_get_set_attribute.phpt b/ext/intl/tests/collator_get_set_attribute.phpt
new file mode 100755
index 0000000000..b234790f81
--- /dev/null
+++ b/ext/intl/tests/collator_get_set_attribute.phpt
@@ -0,0 +1,41 @@
+--TEST--
+get/set_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try to set/get a collation attribute.
+ */
+
+
+/*
+ * Return current normalication mode.
+ */
+function check_val( $coll )
+{
+ $val = ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ return sprintf( "%s\n", ( $val == Collator::OFF ? "off" : "on" ) );
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'en_US' );
+
+ ut_coll_set_attribute( $coll, Collator::NORMALIZATION_MODE, Collator::OFF );
+ $res .= check_val( $coll );
+
+ ut_coll_set_attribute( $coll, Collator::NORMALIZATION_MODE, Collator::ON );
+ $res .= check_val( $coll );
+
+ return $res;
+}
+
+include( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+off
+on
diff --git a/ext/intl/tests/collator_get_set_strength.phpt b/ext/intl/tests/collator_get_set_strength.phpt
new file mode 100755
index 0000000000..1f779441fa
--- /dev/null
+++ b/ext/intl/tests/collator_get_set_strength.phpt
@@ -0,0 +1,41 @@
+--TEST--
+get/set_strength()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try to set/get collation strength.
+ */
+
+/*
+ * Set given collation strength, then get it back
+ * and check if it's the same.
+ */
+function check_set_strength( $coll, $val )
+{
+ ut_coll_set_strength( $coll, $val );
+ $new_val = ut_coll_get_strength( $coll );
+ return ( $new_val == $val ? "ok" : "failed" ) . "\n";
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'en_US' );
+
+ $res .= check_set_strength( $coll, Collator::PRIMARY );
+ $res .= check_set_strength( $coll, Collator::SECONDARY );
+ $res .= check_set_strength( $coll, Collator::TERTIARY );
+
+ return $res;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+ok
+ok
+ok
diff --git a/ext/intl/tests/collator_sort.phpt b/ext/intl/tests/collator_sort.phpt
new file mode 100755
index 0000000000..5cefe2fd7c
--- /dev/null
+++ b/ext/intl/tests/collator_sort.phpt
@@ -0,0 +1,247 @@
+--TEST--
+sort()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort arrays in the given list using specified locale.
+ */
+function sort_arrays( $locale, $arrays, $sort_flag = Collator::SORT_REGULAR )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $arrays as $array )
+ {
+ // Sort array values
+ $res_val = ut_coll_sort( $coll, $array, $sort_flag );
+
+ // Concatenate the sorted array and function result
+ // with output string.
+ $res_dump = "\n" . dump( $array ) .
+ "\n Result: " . dump( $res_val );
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abd', 'aaa' ),
+ array( 'm' , '1' , '_' ),
+ array( 'a' , 'aaa', 'aa' ),
+ array( 'ba' , 'b' , 'ab' ),
+ array( 'e' , 'c' , 'a' ),
+ array( '100', '25' , '36' ),
+ array( 5 , '30' , 2 ),
+ array( 'd' , '' , ' a' ),
+ array( 'd ' , 'f ' , ' a' ),
+ array( 'a' , null , '3' ),
+ array( 'y' , 'k' , 'i' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ $test_params = array(
+ array( '100', '25' , '36' ),
+ array( 5 , '30' , 2 ),
+ array( 'd' , '' , ' a' ),
+ array( 'y' , 'k' , 'i' )
+ );
+
+ // Sort in en_US locale with SORT_STRING flag
+ $res_str .= sort_arrays( 'en_US', $test_params, Collator::SORT_STRING );
+
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'абг', 'абв', 'ааа', 'abc' ),
+ array( 'аа', 'ааа' , 'а' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+ // Sort an array using Lithuanian locale.
+ $test_params = array(
+ array( 'y' , 'k' , 'i' )
+ );
+
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.e8f1cd28133d79ecd660002f1c660d0e:
+array (
+ 0 => 'aaa',
+ 1 => 'abc',
+ 2 => 'abd',
+)
+ Result: true
+
+Test 2.c2ded12173dd2996927378cae37eb275:
+array (
+ 0 => '_',
+ 1 => '1',
+ 2 => 'm',
+)
+ Result: true
+
+Test 3.54071c968d71cb98c5d379145f8d7d38:
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+ Result: true
+
+Test 4.19abe63d6f6dfef65b0e3c9ab4826b07:
+array (
+ 0 => 'ab',
+ 1 => 'b',
+ 2 => 'ba',
+)
+ Result: true
+
+Test 5.9a8dc0a9bc771368c2f1fc3d02754610:
+array (
+ 0 => 'a',
+ 1 => 'c',
+ 2 => 'e',
+)
+ Result: true
+
+Test 6.ab530b060e5e54a65bfb8b9f8fc61870:
+array (
+ 0 => '25',
+ 1 => '36',
+ 2 => '100',
+)
+ Result: true
+
+Test 7.0718dd838509017bded2ed307a6e785f:
+array (
+ 0 => 2,
+ 1 => 5,
+ 2 => '30',
+)
+ Result: true
+
+Test 8.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 9.289bc2f28e87d3201ec9d7e8477ae1b0:
+array (
+ 0 => ' a',
+ 1 => 'd ',
+ 2 => 'f ',
+)
+ Result: true
+
+Test 10.de0fd958484f2377a645835d7fbcf124:
+array (
+ 0 => NULL,
+ 1 => '3',
+ 2 => 'a',
+)
+ Result: true
+
+Test 11.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 12.1e6b4d6f7df9d4580317634ea46d8208:
+array (
+ 0 => '100',
+ 1 => '25',
+ 2 => '36',
+)
+ Result: true
+
+Test 13.cec115dc9850b98dfbdf102efa09e61b:
+array (
+ 0 => 2,
+ 1 => '30',
+ 2 => 5,
+)
+ Result: true
+
+Test 14.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 15.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 16.ca0e38a2e3147dd97070f2128f140934:
+array (
+ 0 => 'abc',
+ 1 => 'ааа',
+ 2 => 'абв',
+ 3 => 'абг',
+)
+ Result: true
+
+Test 17.91480b10473a0c96a4cd6d88c23c577a:
+array (
+ 0 => 'а',
+ 1 => 'аа',
+ 2 => 'ааа',
+)
+ Result: true
+
+Test 18.fdd3fe3981476039164aa000bf9177f2:
+array (
+ 0 => 'i',
+ 1 => 'y',
+ 2 => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/collator_sort_with_sort_keys.phpt b/ext/intl/tests/collator_sort_with_sort_keys.phpt
new file mode 100755
index 0000000000..2f489d745c
--- /dev/null
+++ b/ext/intl/tests/collator_sort_with_sort_keys.phpt
@@ -0,0 +1,189 @@
+--TEST--
+sort_with_sort_keys()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort arrays in the given list using specified locale.
+ */
+function sort_arrays( $locale, $arrays )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $arrays as $array )
+ {
+ // Sort array values
+ $res_val = ut_coll_sort_with_sort_keys( $coll, $array );
+
+ // Concatenate the sorted array and function result
+ // with output string.
+ $res_dump = "\n" . dump( $array ) .
+ "\n Result: " . dump( $res_val );
+
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abd', 'aaa' ),
+ array( 'm' , '1' , '_' ),
+ array( 'a' , 'aaa', 'aa' ),
+ array( 'ba' , 'b' , 'ab' ),
+ array( 'e' , 'c' , 'a' ),
+ array( 'd' , '' , ' a' ),
+ array( 'd ' , 'f ' , ' a' ),
+ array( 'a' , null , '3' ),
+ array( 'y' , 'i' , 'k' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'абг', 'абв', 'ааа', 'abc' ),
+ array( 'аа', 'ааа', 'а' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+ // Array with data for sorting.
+ $test_params = array(
+ array( 'y' , 'i' , 'k' )
+ );
+
+ // Sort an array using Lithuanian locale.
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str . "\n";
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.e8f1cd28133d79ecd660002f1c660d0e:
+array (
+ 0 => 'aaa',
+ 1 => 'abc',
+ 2 => 'abd',
+)
+ Result: true
+
+Test 2.c2ded12173dd2996927378cae37eb275:
+array (
+ 0 => '_',
+ 1 => '1',
+ 2 => 'm',
+)
+ Result: true
+
+Test 3.54071c968d71cb98c5d379145f8d7d38:
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+ Result: true
+
+Test 4.19abe63d6f6dfef65b0e3c9ab4826b07:
+array (
+ 0 => 'ab',
+ 1 => 'b',
+ 2 => 'ba',
+)
+ Result: true
+
+Test 5.9a8dc0a9bc771368c2f1fc3d02754610:
+array (
+ 0 => 'a',
+ 1 => 'c',
+ 2 => 'e',
+)
+ Result: true
+
+Test 6.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 7.289bc2f28e87d3201ec9d7e8477ae1b0:
+array (
+ 0 => ' a',
+ 1 => 'd ',
+ 2 => 'f ',
+)
+ Result: true
+
+Test 8.de0fd958484f2377a645835d7fbcf124:
+array (
+ 0 => NULL,
+ 1 => '3',
+ 2 => 'a',
+)
+ Result: true
+
+Test 9.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 10.ca0e38a2e3147dd97070f2128f140934:
+array (
+ 0 => 'abc',
+ 1 => 'ааа',
+ 2 => 'абв',
+ 3 => 'абг',
+)
+ Result: true
+
+Test 11.91480b10473a0c96a4cd6d88c23c577a:
+array (
+ 0 => 'а',
+ 1 => 'аа',
+ 2 => 'ааа',
+)
+ Result: true
+
+Test 12.fdd3fe3981476039164aa000bf9177f2:
+array (
+ 0 => 'i',
+ 1 => 'y',
+ 2 => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/dateformat_format.phpt b/ext/intl/tests/dateformat_format.phpt
new file mode 100755
index 0000000000..e110c6d151
--- /dev/null
+++ b/ext/intl/tests/dateformat_format.phpt
@@ -0,0 +1,297 @@
+--TEST--
+datefmt_format_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_format function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+
+ $time_arr = array (
+ 0,
+ -1200000,
+ 1200000,
+ 2200000000,
+ -2200000000,
+ 90099999,
+ 3600,
+ -3600
+ );
+
+ $localtime_arr1 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 19,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 105,
+ 'tm_wday' => 0,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+ $localtime_arr2 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 3,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 205,
+ 'tm_wday' => 5,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+ $localtime_arr3 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 3,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => -5,
+ 'tm_wday' => 3,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+
+ $localtime_arr = array (
+ $localtime_arr1,
+ $localtime_arr2,
+ $localtime_arr3
+ );
+
+ //Test format with input as a timestamp : integer
+ foreach( $time_arr as $timestamp_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput timestamp is : $timestamp_entry";
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry )
+ {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ //$fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry,'America/Los_Angeles', IntlDateFormatter::GREGORIAN ,"dd/mm/yyyy");
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ //$fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry);
+ $formatted = ut_datefmt_format( $fmt , $timestamp_entry);
+ $res_str .= "\nFormatted timestamp is : $formatted";
+ }
+ }
+ }
+
+ //Test format with input as a localtime :array
+ foreach( $localtime_arr as $localtime_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput localtime is : ";
+ foreach( $localtime_entry as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry )
+ {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry);
+ $formatted1 = ut_datefmt_format( $fmt , $localtime_entry);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nFormatted localtime_array is : $formatted1";
+ }else{
+ $res_str .= "\nError while formatting as: '".intl_get_error_message()."'";
+ }
+ }
+ }
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+------------
+
+Input timestamp is : 0
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 4:00:00 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 4:00:00 PM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 4:00:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/31/69 4:00 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691231 04:00 PM
+------------
+
+Input timestamp is : -1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 17, 1969 6:40:00 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 17, 1969 6:40:00 PM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 17, 1969 6:40:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/17/69 6:40 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691217 06:40 PM
+------------
+
+Input timestamp is : 1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, January 14, 1970 1:20:00 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 14, 1970 1:20:00 PM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 14, 1970 1:20:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 1/14/70 1:20 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19700114 01:20 PM
+------------
+
+Input timestamp is : 2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Sunday, September 18, 2039 4:06:40 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : September 18, 2039 4:06:40 PM PDT
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Sep 18, 2039 4:06:40 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 9/18/39 4:06 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 20390918 04:06 PM
+------------
+
+Input timestamp is : -2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Saturday, April 14, 1900 5:53:20 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : April 14, 1900 5:53:20 PM PDT
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Apr 14, 1900 5:53:20 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 4/14/00 5:53 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19000414 05:53 PM
+------------
+
+Input timestamp is : 90099999
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, November 8, 1972 11:46:39 AM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : November 8, 1972 11:46:39 AM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Nov 8, 1972 11:46:39 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 11/8/72 11:46 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19721108 11:46 AM
+------------
+
+Input timestamp is : 3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 5:00:00 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 5:00:00 PM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 5:00:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/31/69 5:00 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691231 05:00 PM
+------------
+
+Input timestamp is : -3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 3:00:00 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 3:00:00 PM PST
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 3:00:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/31/69 3:00 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691231 03:00 PM
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_mday : '3' , tm_mon : '3' , tm_year : '105' , tm_wday : '0' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Sunday, April 3, 2005 7:03:24 PM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2005 7:03:24 PM PDT
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 2005 7:03:24 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted localtime_array is : 4/3/05 7:03 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted localtime_array is : 20050403 07:03 PM
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_mday : '3' , tm_mon : '3' , tm_year : '205' , tm_wday : '5' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Friday, April 3, 2105 3:03:24 AM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2105 3:03:24 AM PDT
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 2105 3:03:24 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted localtime_array is : 4/3/05 3:03 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted localtime_array is : 21050403 03:03 AM
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_mday : '3' , tm_mon : '3' , tm_year : '-5' , tm_wday : '3' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Wednesday, April 3, 1895 3:03:24 AM PT
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 1895 3:03:24 AM PDT
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 1895 3:03:24 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted localtime_array is : 4/3/95 3:03 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted localtime_array is : 18950403 03:03 AM
diff --git a/ext/intl/tests/dateformat_format_parse.phpt b/ext/intl/tests/dateformat_format_parse.phpt
new file mode 100755
index 0000000000..ec12de5f0e
--- /dev/null
+++ b/ext/intl/tests/dateformat_format_parse.phpt
@@ -0,0 +1,307 @@
+--TEST--
+datefmt_format_code() and datefmt_parse_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_format function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM
+ );
+
+ $res_str = '';
+
+
+ $time_arr = array (
+ 0,
+ -1200000,
+ 1200000,
+ 2200000000,
+ -2200000000,
+ 90099999,
+ 3600,
+ -3600
+ );
+
+ $localtime_arr1 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 19,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 105,
+ 'tm_wday' => 0,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+ $localtime_arr2 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 3,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 205,
+ 'tm_wday' => 5,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+ $localtime_arr3 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 3,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => -5,
+ 'tm_wday' => 3,
+ 'tm_yday' => 93,
+ 'tm_isdst' => 1
+ );
+
+ $localtime_arr = array (
+ $localtime_arr1,
+ $localtime_arr2,
+ $localtime_arr3
+ );
+
+ //Test format and parse with a timestamp : long
+ foreach( $time_arr as $timestamp_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput timestamp is : $timestamp_entry";
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry ) {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $formatted = ut_datefmt_format( $fmt , $timestamp_entry);
+ $res_str .= "\nFormatted timestamp is : $formatted";
+ $parsed = ut_datefmt_parse( $fmt , $formatted);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nParsed timestamp is : $parsed";
+ }else{
+ $res_str .= "\nError while parsing as: '".intl_get_error_message()."'";
+ }
+ }
+ }
+ }
+
+ //Test format and parse with a localtime :array
+ foreach( $localtime_arr as $localtime_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput localtime is : ";
+ foreach( $localtime_entry as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+
+ $res_str .= "\n------------\n";
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry ) {
+ $res_str .= "\nIntlDateFormatter locale= $locale_entry ,datetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry);
+ $formatted1 = ut_datefmt_format( $fmt , $localtime_entry);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nFormatted localtime_array is : $formatted1";
+ }else{
+ $res_str .= "\nError while formatting as: '".intl_get_error_message()."'";
+ }
+ //Parsing
+ $parsed_arr = ut_datefmt_localtime( $fmt, $formatted1 );
+
+ if( $parsed_arr){
+ $res_str .= "\nParsed array is: ";
+ foreach( $parsed_arr as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+ }
+/*
+ else{
+ //$res_str .= "No values found from LocaleTime parsing.";
+ $res_str .= "\tError : '".intl_get_error_message()."'";
+ }
+*/
+ }
+ }
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+------------
+
+Input timestamp is : 0
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 4:00:00 PM PT
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 4:00:00 PM PST
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 4:00:00 PM
+Parsed timestamp is : 0
+------------
+
+Input timestamp is : -1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 17, 1969 6:40:00 PM PT
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 17, 1969 6:40:00 PM PST
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 17, 1969 6:40:00 PM
+Parsed timestamp is : -1200000
+------------
+
+Input timestamp is : 1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, January 14, 1970 1:20:00 PM PT
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 14, 1970 1:20:00 PM PST
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 14, 1970 1:20:00 PM
+Parsed timestamp is : 1200000
+------------
+
+Input timestamp is : 2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Sunday, September 18, 2039 4:06:40 PM PT
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : September 18, 2039 4:06:40 PM PDT
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Sep 18, 2039 4:06:40 PM
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+------------
+
+Input timestamp is : -2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Saturday, April 14, 1900 5:53:20 PM PT
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : April 14, 1900 5:53:20 PM PDT
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Apr 14, 1900 5:53:20 PM
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+------------
+
+Input timestamp is : 90099999
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, November 8, 1972 11:46:39 AM PT
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : November 8, 1972 11:46:39 AM PST
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Nov 8, 1972 11:46:39 AM
+Parsed timestamp is : 90099999
+------------
+
+Input timestamp is : 3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 5:00:00 PM PT
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 5:00:00 PM PST
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 5:00:00 PM
+Parsed timestamp is : 3600
+------------
+
+Input timestamp is : -3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 3:00:00 PM PT
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 3:00:00 PM PST
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 3:00:00 PM
+Parsed timestamp is : -3600
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_mday : '3' , tm_mon : '3' , tm_year : '105' , tm_wday : '0' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Sunday, April 3, 2005 7:03:24 PM PT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2005 7:03:24 PM PDT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 2005 7:03:24 PM
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_year : '105' , tm_mday : '3' , tm_wday : '0' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_mday : '3' , tm_mon : '3' , tm_year : '205' , tm_wday : '5' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Friday, April 3, 2105 3:03:24 AM PT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '205' , tm_mday : '3' , tm_wday : '5' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2105 3:03:24 AM PDT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '205' , tm_mday : '3' , tm_wday : '5' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 2105 3:03:24 AM
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '205' , tm_mday : '3' , tm_wday : '5' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_mday : '3' , tm_mon : '3' , tm_year : '-5' , tm_wday : '3' , tm_yday : '93' , tm_isdst : '1' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Wednesday, April 3, 1895 3:03:24 AM PT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '-5' , tm_mday : '3' , tm_wday : '3' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 1895 3:03:24 AM PDT
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '-5' , tm_mday : '3' , tm_wday : '3' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Apr 3, 1895 3:03:24 AM
+Parsed array is: tm_sec : '24' , tm_min : '3' , tm_hour : '3' , tm_year : '-5' , tm_mday : '3' , tm_wday : '3' , tm_yday : '93' , tm_mon : '3' , tm_isdst : '1' , \ No newline at end of file
diff --git a/ext/intl/tests/dateformat_get_datetype.phpt b/ext/intl/tests/dateformat_get_datetype.phpt
new file mode 100755
index 0000000000..24d03898ea
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_datetype.phpt
@@ -0,0 +1,57 @@
+--TEST--
+datefmt_get_datetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_datetype function
+ */
+
+
+function ut_main()
+{
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+ foreach( $datetype_arr as $datetype_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with date_type = $datetype_entry";
+ $fmt = ut_datefmt_create( "de-DE", $datetype_entry , IntlDateFormatter::SHORT,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $date_type = ut_datefmt_get_datetype( $fmt);
+ $res_str .= "\nAfter call to get_datetype : datetype= $date_type";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with date_type = 0
+After call to get_datetype : datetype= 0
+
+Creating IntlDateFormatter with date_type = 1
+After call to get_datetype : datetype= 1
+
+Creating IntlDateFormatter with date_type = 2
+After call to get_datetype : datetype= 2
+
+Creating IntlDateFormatter with date_type = 3
+After call to get_datetype : datetype= 3
+
+Creating IntlDateFormatter with date_type = -1
+After call to get_datetype : datetype= -1
diff --git a/ext/intl/tests/dateformat_get_locale.phpt b/ext/intl/tests/dateformat_get_locale.phpt
new file mode 100755
index 0000000000..2626972618
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_locale.phpt
@@ -0,0 +1,53 @@
+--TEST--
+datefmt_get_locale_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_locale function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'de-DE',
+ 'sl-IT-nedis',
+ 'en_UK',
+ 'hi'
+ );
+
+ $res_str = '';
+
+ foreach( $locale_arr as $locale_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with locale = $locale_entry";
+ $fmt = ut_datefmt_create( $locale_entry , IntlDateFormatter::SHORT,IntlDateFormatter::SHORT,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $locale = ut_datefmt_get_locale( $fmt , 1);
+ $res_str .= "\nAfter call to get_locale : locale= $locale";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with locale = de-DE
+After call to get_locale : locale= de_DE
+
+Creating IntlDateFormatter with locale = sl-IT-nedis
+After call to get_locale : locale= sl
+
+Creating IntlDateFormatter with locale = en_UK
+After call to get_locale : locale= en
+
+Creating IntlDateFormatter with locale = hi
+After call to get_locale : locale= hi
diff --git a/ext/intl/tests/dateformat_get_set_calendar.phpt b/ext/intl/tests/dateformat_get_set_calendar.phpt
new file mode 100755
index 0000000000..bfd4e578e1
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_set_calendar.phpt
@@ -0,0 +1,60 @@
+--TEST--
+datefmt_get_calendar_code() datefmt_set_calendar_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_calendar and datefmt_set_calendar functions
+ */
+
+
+function ut_main()
+{
+ $calendar_arr = array (
+ IntlDateFormatter::GREGORIAN,
+ IntlDateFormatter::TRADITIONAL,
+ 3
+ );
+
+ $res_str = '';
+
+ $start_calendar = IntlDateFormatter::GREGORIAN;
+ $res_str .= "\nCreating IntlDateFormatter with calendar = $start_calendar";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT ,'America/Los_Angeles', IntlDateFormatter::GREGORIAN);
+ $calendar = ut_datefmt_get_calendar( $fmt);
+ $res_str .= "\nAfter call to get_calendar : calendar= $calendar";
+ $res_str .= "\n-------------------";
+
+ foreach( $calendar_arr as $calendar_entry )
+ {
+ $res_str .= "\nSetting IntlDateFormatter with calendar = $calendar_entry";
+ ut_datefmt_set_calendar( $fmt, $calendar_entry);
+ $calendar = ut_datefmt_get_calendar( $fmt);
+ $res_str .= "\nAfter call to get_calendar : calendar= $calendar";
+ $res_str .= "\n-------------------";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with calendar = 1
+After call to get_calendar : calendar= 1
+-------------------
+Setting IntlDateFormatter with calendar = 1
+After call to get_calendar : calendar= 1
+-------------------
+Setting IntlDateFormatter with calendar = 0
+After call to get_calendar : calendar= 0
+-------------------
+Setting IntlDateFormatter with calendar = 3
+After call to get_calendar : calendar= 0
+------------------- \ No newline at end of file
diff --git a/ext/intl/tests/dateformat_get_set_pattern.phpt b/ext/intl/tests/dateformat_get_set_pattern.phpt
new file mode 100755
index 0000000000..b0d02dc0b4
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_set_pattern.phpt
@@ -0,0 +1,84 @@
+--TEST--
+datefmt_get_pattern_code and datefmt_set_pattern_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+
+<?php
+
+/*
+ * Test for the datefmt_get_pattern & datefmt_set_pattern function
+ */
+
+
+function ut_main()
+{
+ $pattern_arr = array (
+ 'DD-MM-YYYY hh:mm:ss',
+ 'yyyy-DDD.hh:mm:ss z',
+ "yyyy/MM/dd",
+ "yyyyMMdd"
+ );
+
+ $res_str = '';
+
+ $start_pattern = 'dd-MM-YY';
+ $res_str .= "\nCreating IntlDateFormatter with pattern = $start_pattern ";
+ //$fmt = ut_datefmt_create( "en-US", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT , 'America/New_York', IntlDateFormatter::GREGORIAN , $start_pattern );
+ $fmt = ut_datefmt_create( "en-US", IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/New_York', IntlDateFormatter::GREGORIAN , $start_pattern );
+ $pattern = ut_datefmt_get_pattern( $fmt);
+ $res_str .= "\nAfter call to get_pattern : pattern= $pattern";
+ $formatted = ut_datefmt_format($fmt,0);
+ $res_str .= "\nResult of formatting timestamp=0 is : \n$formatted";
+
+
+ foreach( $pattern_arr as $pattern_entry )
+ {
+ $res_str .= "\n-------------------";
+ $res_str .= "\nSetting IntlDateFormatter with pattern = $pattern_entry ";
+ ut_datefmt_set_pattern( $fmt , $pattern_entry );
+ $pattern = ut_datefmt_get_pattern( $fmt);
+ $res_str .= "\nAfter call to get_pattern : pattern= $pattern";
+ $formatted = ut_datefmt_format($fmt,0);
+ $res_str .= "\nResult of formatting timestamp=0 with the new pattern is : \n$formatted";
+ $res_str .= "\n";
+
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with pattern = dd-MM-YY
+After call to get_pattern : pattern= dd-MM-YY
+Result of formatting timestamp=0 is :
+31-12-70
+-------------------
+Setting IntlDateFormatter with pattern = DD-MM-YYYY hh:mm:ss
+After call to get_pattern : pattern= DD-MM-YYYY hh:mm:ss
+Result of formatting timestamp=0 with the new pattern is :
+365-12-1970 07:00:00
+
+-------------------
+Setting IntlDateFormatter with pattern = yyyy-DDD.hh:mm:ss z
+After call to get_pattern : pattern= yyyy-DDD.hh:mm:ss z
+Result of formatting timestamp=0 with the new pattern is :
+1969-365.07:00:00 EST
+
+-------------------
+Setting IntlDateFormatter with pattern = yyyy/MM/dd
+After call to get_pattern : pattern= yyyy/MM/dd
+Result of formatting timestamp=0 with the new pattern is :
+1969/12/31
+
+-------------------
+Setting IntlDateFormatter with pattern = yyyyMMdd
+After call to get_pattern : pattern= yyyyMMdd
+Result of formatting timestamp=0 with the new pattern is :
+19691231 \ No newline at end of file
diff --git a/ext/intl/tests/dateformat_get_timetype.phpt b/ext/intl/tests/dateformat_get_timetype.phpt
new file mode 100755
index 0000000000..2ddc1172b1
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_timetype.phpt
@@ -0,0 +1,57 @@
+--TEST--
+datefmt_get_timetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_timetype function
+ */
+
+
+function ut_main()
+{
+ $timetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+ foreach( $timetype_arr as $timetype_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with time_type = $timetype_entry";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, $timetype_entry ,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $time_type = ut_datefmt_get_timetype( $fmt);
+ $res_str .= "\nAfter call to get_timetype : timetype= $time_type";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with time_type = 0
+After call to get_timetype : timetype= 0
+
+Creating IntlDateFormatter with time_type = 1
+After call to get_timetype : timetype= 1
+
+Creating IntlDateFormatter with time_type = 2
+After call to get_timetype : timetype= 2
+
+Creating IntlDateFormatter with time_type = 3
+After call to get_timetype : timetype= 3
+
+Creating IntlDateFormatter with time_type = -1
+After call to get_timetype : timetype= -1
diff --git a/ext/intl/tests/dateformat_get_timezone_id.phpt b/ext/intl/tests/dateformat_get_timezone_id.phpt
new file mode 100755
index 0000000000..80cbdbbf0f
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_timezone_id.phpt
@@ -0,0 +1,49 @@
+--TEST--
+datefmt_get_timezone_id_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_timezone_id function
+ */
+
+
+function ut_main()
+{
+ $timezone_id_arr = array (
+ 'America/New_York',
+ 'America/Los_Angeles',
+ 'America/Dallas'
+ );
+
+ $res_str = '';
+
+ foreach( $timezone_id_arr as $timezone_id_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with timezone_id = $timezone_id_entry";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, $timezone_id_entry , IntlDateFormatter::GREGORIAN );
+ $timezone_id = ut_datefmt_get_timezone_id( $fmt);
+ $res_str .= "\nAfter call to get_timezone_id : timezone_id= $timezone_id";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with timezone_id = America/New_York
+After call to get_timezone_id : timezone_id= America/New_York
+
+Creating IntlDateFormatter with timezone_id = America/Los_Angeles
+After call to get_timezone_id : timezone_id= America/Los_Angeles
+
+Creating IntlDateFormatter with timezone_id = America/Dallas
+After call to get_timezone_id : timezone_id= America/Dallas
diff --git a/ext/intl/tests/dateformat_is_set_lenient.phpt b/ext/intl/tests/dateformat_is_set_lenient.phpt
new file mode 100755
index 0000000000..8492a2e2a8
--- /dev/null
+++ b/ext/intl/tests/dateformat_is_set_lenient.phpt
@@ -0,0 +1,88 @@
+--TEST--
+datefmt_set_lenient and datefmt_set_lenient()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+
+<?php
+
+/*
+ * Test for the datefmt_get_lenient & datefmt_set_lenient function
+ */
+
+
+function ut_main()
+{
+
+ $res_str = '';
+
+ //Create
+ $fmt = ut_datefmt_create( "en-US", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT , 'America/New_York', IntlDateFormatter::GREGORIAN );
+ $res_str .= "\nIntlDateFormatter Created.\n";
+
+ $resLenient1 = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to get_lenient : lenient= ";
+ if( $resLenient1){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+ //Set and test
+ $res_str .= "--------------------\n";
+ $isLenient = TRUE;
+ $res_str .= "Setting IntlDateFormatter with lenient = ";
+ if( $isLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+ ut_datefmt_set_lenient( $fmt , $isLenient );
+ $resLenient = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to is_lenient : lenient= ";
+ if( $resLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+
+ //Set and test
+ $res_str .= "--------------------\n";
+ $isLenient = FALSE;
+ $res_str .= "Setting IntlDateFormatter with lenient =";
+ if( $isLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+ ut_datefmt_set_lenient( $fmt , $isLenient);
+ $resLenient = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to is_lenient : lenient= ";
+ if( $resLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+ $res_str .= "--------------------\n";
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+IntlDateFormatter Created.
+After call to get_lenient : lenient= TRUE
+--------------------
+Setting IntlDateFormatter with lenient = TRUE
+After call to is_lenient : lenient= TRUE
+--------------------
+Setting IntlDateFormatter with lenient =FALSE
+After call to is_lenient : lenient= FALSE
+--------------------
diff --git a/ext/intl/tests/dateformat_localtime.phpt b/ext/intl/tests/dateformat_localtime.phpt
new file mode 100755
index 0000000000..6722716ca9
--- /dev/null
+++ b/ext/intl/tests/dateformat_localtime.phpt
@@ -0,0 +1,133 @@
+--TEST--
+datefmt_localtime_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_localtime function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US_CA'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ );
+
+ $res_str = '';
+
+
+ $text_arr = array (
+ "Thursday, December 18, 1969 8:49:59 AM PST",
+ "June 18, 1969 8:49:59 AM ",
+ "12/18/69 8:49 AM",
+ "19691218 08:49 AM"
+ );
+
+ $fmt1 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::LONG, IntlDateFormatter::LONG);
+ $fmt2 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::MEDIUM, IntlDateFormatter::MEDIUM);
+ $fmt3 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::FULL, IntlDateFormatter::FULL);
+ $fmt_array = array(
+ $fmt1 , $fmt2 ,$fmt3
+ );
+ $fmt_desc_array = array(
+ "DateType::LONG, TimeType::LONG",
+ "DateType::MEDIUM, TimeType::MEDIUM",
+ "DateType::FULL, TimeType::FULL"
+ );
+
+ foreach( $text_arr as $text_entry){
+ $res_str .= "\n-------------------------------\n";
+ $res_str .= "\nInput text is : $text_entry";
+ $cnt =0;
+
+ $parse_pos = 0;
+
+ foreach( $fmt_array as $fmt_entry ){
+ $res_str .= "\n------------";
+ $res_str .= "\nIntlDateFormatter : ".$fmt_desc_array[$cnt];
+ $cnt++;
+ $parsed_arr = ut_datefmt_localtime( $fmt_entry , $text_entry , $parse_pos );
+
+ if( $parsed_arr){
+ $res_str .= "\n";
+ foreach( $parsed_arr as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+ }
+/*
+ else{
+ //$res_str .= "No values found from LocaleTime parsing.";
+ $res_str .= "\tError : '".intl_get_error_message()."'";
+ }
+*/
+ }//end of for $fmt_array
+ }
+
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+-------------------------------
+
+Input text is : Thursday, December 18, 1969 8:49:59 AM PST
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL
+tm_sec : '59' , tm_min : '49' , tm_hour : '8' , tm_year : '69' , tm_mday : '18' , tm_wday : '4' , tm_yday : '352' , tm_mon : '11' , tm_isdst : '0' ,
+-------------------------------
+
+Input text is : June 18, 1969 8:49:59 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM
+tm_sec : '59' , tm_min : '49' , tm_hour : '8' , tm_year : '69' , tm_mday : '18' , tm_wday : '3' , tm_yday : '169' , tm_mon : '5' , tm_isdst : '1' ,
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL
+-------------------------------
+
+Input text is : 12/18/69 8:49 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL
+-------------------------------
+
+Input text is : 19691218 08:49 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL \ No newline at end of file
diff --git a/ext/intl/tests/dateformat_parse.phpt b/ext/intl/tests/dateformat_parse.phpt
new file mode 100755
index 0000000000..a4289786eb
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse.phpt
@@ -0,0 +1,82 @@
+--TEST--
+datefmt_parse_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_parse function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US_CA'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+
+ $text_arr = array (
+ "Sunday, September 18, 2039 4:06:40 PM PT",
+ "Wednesday, December 17, 1969 6:40:00 PM PT",
+ "Thursday, December 18, 1969 8:49:59 AM PST",
+ "Thursday, December 18, 1969 8:49:59 PM PST",
+ "December 18, 1969 8:49:59 AM PST",
+ "12/18/69 8:49 AM",
+ "19691218 08:49 AM"
+ );
+
+ $parse_pos = 0;
+ foreach( $text_arr as $text_entry){
+ for ( $parse_pos = 0 ; $parse_pos < strlen($text_entry) ; $parse_pos++ ){
+ $fmt = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::NONE, IntlDateFormatter::SHORT);
+ $parsed = ut_datefmt_parse( $fmt , $text_entry , $parse_pos );
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nInput text :$text_entry ; Parsed text : $parsed";
+ $res_str .= " ; parse_pos : $parse_pos";
+ }
+/*
+ else{
+ $res_str .= "\nError while parsing as: '".intl_get_error_message()."'";
+ }
+*/
+ }
+ }
+
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Input text :Sunday, September 18, 2039 4:06:40 PM PT ; Parsed text : 96000 ; parse_pos : 29
+Input text :Sunday, September 18, 2039 4:06:40 PM PT ; Parsed text : 96000 ; parse_pos : 30
+Input text :Wednesday, December 17, 1969 6:40:00 PM PT ; Parsed text : 216000 ; parse_pos : 31
+Input text :Wednesday, December 17, 1969 6:40:00 PM PT ; Parsed text : 72000 ; parse_pos : 32
+Input text :Thursday, December 18, 1969 8:49:59 AM PST ; Parsed text : 208740 ; parse_pos : 30
+Input text :Thursday, December 18, 1969 8:49:59 AM PST ; Parsed text : 64740 ; parse_pos : 31
+Input text :Thursday, December 18, 1969 8:49:59 PM PST ; Parsed text : 251940 ; parse_pos : 30
+Input text :Thursday, December 18, 1969 8:49:59 PM PST ; Parsed text : 107940 ; parse_pos : 31
+Input text :December 18, 1969 8:49:59 AM PST ; Parsed text : 208740 ; parse_pos : 20
+Input text :December 18, 1969 8:49:59 AM PST ; Parsed text : 64740 ; parse_pos : 21
+Input text :12/18/69 8:49 AM ; Parsed text : 60540 ; parse_pos : 8
+Input text :12/18/69 8:49 AM ; Parsed text : 60540 ; parse_pos : 9
+Input text :19691218 08:49 AM ; Parsed text : 60540 ; parse_pos : 8
+Input text :19691218 08:49 AM ; Parsed text : 60540 ; parse_pos : 9
+Input text :19691218 08:49 AM ; Parsed text : 60540 ; parse_pos : 10
diff --git a/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt b/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt
new file mode 100755
index 0000000000..b19ab7776f
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt
@@ -0,0 +1,119 @@
+--TEST--
+datefmt_parse_localtime() with parse pos
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_parse_localtime function with parse pos
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US_CA'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ );
+
+ $res_str = '';
+
+
+ $text_arr = array (
+ "Thursday, December 18, 1969 8:49:59 AM PST",
+ "June 18, 1969 8:49:59 AM ",
+ "12/18/69 8:49 AM",
+ "19691218 08:49 AM"
+ );
+
+ $fmt1 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::LONG, IntlDateFormatter::LONG);
+ $fmt2 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::MEDIUM, IntlDateFormatter::MEDIUM);
+ $fmt3 = ut_datefmt_create( 'en_US_CA', IntlDateFormatter::FULL, IntlDateFormatter::FULL);
+ $fmt_array = array(
+ $fmt1 , $fmt2 ,$fmt3
+ );
+ $fmt_desc_array = array(
+ "DateType::LONG, TimeType::LONG",
+ "DateType::MEDIUM, TimeType::MEDIUM",
+ "DateType::FULL, TimeType::FULL"
+ );
+
+ foreach( $text_arr as $text_entry){
+ $res_str .= "\n-------------------------------\n";
+ $res_str .= "\nInput text is : $text_entry";
+ $cnt =0;
+ foreach( $fmt_array as $fmt_entry ){
+ $res_str .= "\n------------";
+ $res_str .= "\nIntlDateFormatter : ".$fmt_desc_array[$cnt];
+ $cnt++;
+ $parsed_arr = ut_datefmt_localtime( $fmt_entry , $text_entry);
+
+ if( $parsed_arr){
+ $res_str .= "\n";
+ foreach( $parsed_arr as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+ }
+ else{
+ //$res_str .= "No values found from LocaleTime parsing.";
+ $res_str .= "\tError : '".intl_get_error_message()."'";
+ }
+
+ }
+ }
+
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+-------------------------------
+
+Input text is : Thursday, December 18, 1969 8:49:59 AM PST
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL
+tm_sec : '59' , tm_min : '49' , tm_hour : '8' , tm_year : '69' , tm_mday : '18' , tm_wday : '4' , tm_yday : '352' , tm_mon : '11' , tm_isdst : '0' ,
+-------------------------------
+
+Input text is : June 18, 1969 8:49:59 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM
+tm_sec : '59' , tm_min : '49' , tm_hour : '8' , tm_year : '69' , tm_mday : '18' , tm_wday : '3' , tm_yday : '169' , tm_mon : '5' , tm_isdst : '1' ,
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL Error : 'Date parsing failed: U_PARSE_ERROR'
+-------------------------------
+
+Input text is : 12/18/69 8:49 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL Error : 'Date parsing failed: U_PARSE_ERROR'
+-------------------------------
+
+Input text is : 19691218 08:49 AM
+------------
+IntlDateFormatter : DateType::LONG, TimeType::LONG Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::MEDIUM, TimeType::MEDIUM Error : 'Date parsing failed: U_PARSE_ERROR'
+------------
+IntlDateFormatter : DateType::FULL, TimeType::FULL Error : 'Date parsing failed: U_PARSE_ERROR'
diff --git a/ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt b/ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt
new file mode 100755
index 0000000000..fa40257372
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt
@@ -0,0 +1,137 @@
+--TEST--
+datefmt_parse_timestamp_code() with parse pos
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_parse_timestamp function with parse_pos
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'en_US_CA'
+ );
+
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+
+ $text_arr = array (
+ "Sunday, September 18, 3039 4:06:40 PM PT",
+ "Thursday, December 18, 1969 8:49:59 AM PST",
+ //"December 18, 1969 8:49:59 AM PST",
+ //"12/18/69 8:49 AM",
+ "19001218 08:49 AM",
+ "19691218 08:49 AM"
+ );
+
+ foreach( $text_arr as $text_entry){
+ $res_str .= "\n------------\n";
+ $res_str .= "\nInput text is : $text_entry";
+ $res_str .= "\n------------";
+
+ foreach( $locale_arr as $locale_entry ){
+ $res_str .= "\nLocale is : $locale_entry";
+ $res_str .= "\n------------";
+ foreach( $datetype_arr as $datetype_entry )
+ {
+ $res_str .= "\ndatetype = $datetype_entry ,timetype =$datetype_entry ";
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry);
+ $parsed = ut_datefmt_parse( $fmt , $text_entry);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nParsed text is : $parsed";
+ }else{
+ $res_str .= "\nError while parsing as: '".intl_get_error_message()."'";
+ }
+ }
+ }
+ }
+
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+------------
+
+Input text is : Sunday, September 18, 3039 4:06:40 PM PT
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = -1 ,timetype =-1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+------------
+
+Input text is : Thursday, December 18, 1969 8:49:59 AM PST
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Parsed text is : -1149001
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = -1 ,timetype =-1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+------------
+
+Input text is : 19001218 08:49 AM
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = -1 ,timetype =-1
+Error while parsing as: 'datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.
+The valid range of a timestamp is typically from Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT.: U_BUFFER_OVERFLOW_ERROR'
+------------
+
+Input text is : 19691218 08:49 AM
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'
+datetype = -1 ,timetype =-1
+Parsed text is : -1149060
diff --git a/ext/intl/tests/dateformat_set_timezone_id.phpt b/ext/intl/tests/dateformat_set_timezone_id.phpt
new file mode 100755
index 0000000000..76b4d474c7
--- /dev/null
+++ b/ext/intl/tests/dateformat_set_timezone_id.phpt
@@ -0,0 +1,75 @@
+--TEST--
+datefmt_set_timezone_id_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_set_timezone_id function
+ */
+
+
+function ut_main()
+{
+ $timezone_id_arr = array (
+ 'America/New_York',
+ 'America/Los_Angeles',
+ 'America/Chicago',
+ 'CN'
+ );
+ $timestamp_entry = 0;
+
+ $res_str = '';
+
+ $fmt = ut_datefmt_create( "en_US", IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/San_Francisco' , IntlDateFormatter::GREGORIAN );
+ $timezone_id = ut_datefmt_get_timezone_id( $fmt );
+ $res_str .= "\nAfter creation of the dateformatter : timezone_id= $timezone_id\n";
+
+ foreach( $timezone_id_arr as $timezone_id_entry )
+ {
+
+ $res_str .= "-----------";
+ $res_str .= "\nTrying to set timezone_id= $timezone_id_entry";
+ ut_datefmt_set_timezone_id( $fmt , $timezone_id_entry );
+ $timezone_id = ut_datefmt_get_timezone_id( $fmt );
+ $res_str .= "\nAfter call to set_timezone_id : timezone_id= $timezone_id";
+ $formatted = ut_datefmt_format( $fmt, 0);
+ $res_str .= "\nFormatting timestamp=0 resulted in $formatted";
+ $formatted = ut_datefmt_format( $fmt, 3600);
+ $res_str .= "\nFormatting timestamp=3600 resulted in $formatted";
+ $res_str .= "\n";
+
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+After creation of the dateformatter : timezone_id= America/San_Francisco
+-----------
+Trying to set timezone_id= America/New_York
+After call to set_timezone_id : timezone_id= America/New_York
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 7:00:00 PM ET
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 8:00:00 PM ET
+-----------
+Trying to set timezone_id= America/Los_Angeles
+After call to set_timezone_id : timezone_id= America/Los_Angeles
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 4:00:00 PM PT
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 5:00:00 PM PT
+-----------
+Trying to set timezone_id= America/Chicago
+After call to set_timezone_id : timezone_id= America/Chicago
+Formatting timestamp=0 resulted in Wednesday, December 31, 1969 6:00:00 PM CT
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 7:00:00 PM CT
+-----------
+Trying to set timezone_id= CN
+After call to set_timezone_id : timezone_id= CN
+Formatting timestamp=0 resulted in Thursday, January 1, 1970 12:00:00 AM GMT+00:00
+Formatting timestamp=3600 resulted in Thursday, January 1, 1970 1:00:00 AM GMT+00:00 \ No newline at end of file
diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt
new file mode 100755
index 0000000000..c313df57a6
--- /dev/null
+++ b/ext/intl/tests/formatter_fail.phpt
@@ -0,0 +1,79 @@
+--TEST--
+numfmt creation failures
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function err($fmt) {
+ if(!$fmt) {
+ echo var_export(intl_get_error_message(), true)."\n";
+ }
+}
+
+function crt($t, $l, $s) {
+ switch(true) {
+ case $t == "O":
+ return new NumberFormatter($l, $s);
+ break;
+ case $t == "C":
+ return NumberFormatter::create($l, $s);
+ break;
+ case $t == "P":
+ return numfmt_create($l, $s);
+ break;
+ }
+}
+
+$args = array(
+ array(null, null),
+ array("whatever", 1234567),
+ array(array(), array()),
+ array("en", -1),
+ array("en_US", NumberFormatter::PATTERN_RULEBASED),
+);
+
+$fmt = new NumberFormatter();
+err($fmt);
+$fmt = numfmt_create();
+err($fmt);
+$fmt = NumberFormatter::create();
+err($fmt);
+
+foreach($args as $arg) {
+ $fmt = crt("O", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("C", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("P", $arg[0], $arg[1]);
+ err($fmt);
+}
+
+?>
+--EXPECTF--
+Warning: NumberFormatter::__construct() expects at least 2 parameters, 0 given in %s on line %d
+'__construct: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: numfmt_create() expects at least 2 parameters, 0 given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: NumberFormatter::create() expects at least 2 parameters, 0 given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+
+Warning: NumberFormatter::__construct() expects parameter 1 to be string, array given in %s on line %d
+'__construct: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: NumberFormatter::create() expects parameter 1 to be string, array given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: numfmt_create() expects parameter 1 to be string, array given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'__construct: number formatter creation failed: U_MEMORY_ALLOCATION_ERROR'
+'numfmt_create: number formatter creation failed: U_MEMORY_ALLOCATION_ERROR'
+'numfmt_create: number formatter creation failed: U_MEMORY_ALLOCATION_ERROR' \ No newline at end of file
diff --git a/ext/intl/tests/formatter_format.phpt b/ext/intl/tests/formatter_format.phpt
new file mode 100755
index 0000000000..4483d83a5f
--- /dev/null
+++ b/ext/intl/tests/formatter_format.phpt
@@ -0,0 +1,108 @@
+--TEST--
+numfmt_format()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+
+function ut_main()
+{
+ $styles = array(
+ NumberFormatter::PATTERN_DECIMAL => '##.#####################',
+ NumberFormatter::DECIMAL => '',
+ NumberFormatter::CURRENCY => '',
+ NumberFormatter::PERCENT => '',
+ NumberFormatter::SCIENTIFIC => '',
+ NumberFormatter::SPELLOUT => '@@@@@@@',
+ NumberFormatter::ORDINAL => '',
+ NumberFormatter::DURATION => '',
+ NumberFormatter::PATTERN_RULEBASED => '#####.###',
+ 1234999, // bad one
+ );
+
+ $locales = array(
+ 'en_US',
+ 'ru_UA',
+ 'de',
+ 'en_UK'
+ );
+
+ $str_res = '';
+ $number = 1234567.891234567890000;
+
+ foreach( $locales as $locale )
+ {
+ $str_res .= "\n Locale is: $locale\n";
+ foreach( $styles as $style => $pattern )
+ {
+ $fmt = ut_nfmt_create( $locale, $style, $pattern );
+
+ if(!$fmt) {
+ $str_res .= "Bad formatter!\n";
+ continue;
+ }
+ $str_res .= dump( ut_nfmt_format( $fmt, $number ) ) . "\n";
+ }
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Locale is: en_US
+'1234567.89123457'
+'1,234,567.891'
+'$1,234,567.89'
+'123,456,789%'
+'1.23456789123457E6'
+'one million, two hundred and thirty-four thousand, five hundred and sixty-seven point eight nine one two three four five seven'
+'1,234,568th'
+'342:56:08'
+'#####.###'
+Bad formatter!
+
+ Locale is: ru_UA
+'1234567,89123457'
+'1 234 567,891'
+'1 234 567,89 грн.'
+'123 456 789%'
+'1,23456789123457E6'
+'миллион два сто тридцать четыре тысяча пять сто шестьдесят восемь'
+'1 234 568'
+'1 234 568'
+'#####.###'
+Bad formatter!
+
+ Locale is: de
+'1234567,89123457'
+'1.234.567,891'
+'¤ 1.234.567,89'
+'123.456.789%'
+'1,23456789123457E6'
+'eine Million zweihundertvierunddreißigtausendfünfhundertsiebenundsechzig komma acht neun eins zwei drei vier fünf sieben'
+'1.234.568'
+'1.234.568'
+'#####.###'
+Bad formatter!
+
+ Locale is: en_UK
+'1234567.89123457'
+'1,234,567.891'
+'¤1,234,567.89'
+'123,456,789%'
+'1.23456789123457E6'
+'one million, two hundred and thirty-four thousand, five hundred and sixty-seven point eight nine one two three four five seven'
+'1,234,568th'
+'342:56:08'
+'#####.###'
+Bad formatter!
diff --git a/ext/intl/tests/formatter_format_conv.phpt b/ext/intl/tests/formatter_format_conv.phpt
new file mode 100755
index 0000000000..e1d25ef3d9
--- /dev/null
+++ b/ext/intl/tests/formatter_format_conv.phpt
@@ -0,0 +1,24 @@
+--TEST--
+numfmt_format() with type conversion
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function ut_main()
+{
+ $fmt = ut_nfmt_create( 'en_US', NumberFormatter::DECIMAL );
+ $number = 1234567.891234567890000;
+
+ $str_res = ut_nfmt_format ($fmt, $number, NumberFormatter::TYPE_INT32)."\n";
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+1,234,567
diff --git a/ext/intl/tests/formatter_format_currency.phpt b/ext/intl/tests/formatter_format_currency.phpt
new file mode 100755
index 0000000000..b9c2e50dd6
--- /dev/null
+++ b/ext/intl/tests/formatter_format_currency.phpt
@@ -0,0 +1,45 @@
+--TEST--
+numfmt_format_currency()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc currencies/locales.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK' => 'GBP',
+ 'en_US' => 'USD',
+ 'ru' => 'RUR',
+ 'uk' => 'UAH',
+ 'en' => 'UAH'
+ );
+
+ $res_str = '';
+ $number = 1234567.89;
+
+ foreach( $locales as $locale => $currency )
+ {
+ $fmt = ut_nfmt_create( $locale, NumberFormatter::CURRENCY );
+ $res_str .= "$locale: " . var_export( ut_nfmt_format_currency( $fmt, $number, $currency ), true ) . "\n";
+ }
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+en_UK: '£1,234,567.89'
+en_US: '$1,234,567.89'
+ru: '1 234 567,89р.'
+uk: 'грн. 1 234 567,89'
+en: 'UAH1,234,567.89'
diff --git a/ext/intl/tests/formatter_get_error.phpt b/ext/intl/tests/formatter_get_error.phpt
new file mode 100755
index 0000000000..c7b3972709
--- /dev/null
+++ b/ext/intl/tests/formatter_get_error.phpt
@@ -0,0 +1,31 @@
+--TEST--
+numfmt_get_error_message/code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Error handling.
+ */
+
+
+function ut_main()
+{
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $currency = '';
+ $pos = 0;
+ $num = ut_nfmt_parse_currency( $fmt, '123.45', $currency, $pos );
+ if( $num === false )
+ return $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n";
+ else
+ return "Ooops, an error should have occured.";
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Number parsing failed: U_PARSE_ERROR (9)
diff --git a/ext/intl/tests/formatter_get_locale.phpt b/ext/intl/tests/formatter_get_locale.phpt
new file mode 100755
index 0000000000..1a9b780124
--- /dev/null
+++ b/ext/intl/tests/formatter_get_locale.phpt
@@ -0,0 +1,49 @@
+--TEST--
+numfmt_get_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get locale.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK',
+ 'en_US@California',
+ 'uk',
+ );
+
+ $loc_types = array(
+ Locale::ACTUAL_LOCALE => 'actual',
+ Locale::VALID_LOCALE => 'valid',
+ );
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $fmt = ut_nfmt_create( $locale, NumberFormatter::DECIMAL );
+ $res_str .= "$locale: ";
+ foreach( $loc_types as $loc_type => $loc_type_name )
+ $res_str .= sprintf( " %s=%s",
+ $loc_type_name,
+ dump( ut_nfmt_get_locale( $fmt, $loc_type ) ) );
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+en_UK: actual='en' valid='en'
+en_US@California: actual='en' valid='en'
+uk: actual='root' valid='uk'
diff --git a/ext/intl/tests/formatter_get_set_attribute.phpt b/ext/intl/tests/formatter_get_set_attribute.phpt
new file mode 100755
index 0000000000..c523346841
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_attribute.phpt
@@ -0,0 +1,193 @@
+--TEST--
+numfmt_get/set_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set various number formatting attributes.
+ */
+
+
+function ut_main()
+{
+ // attr_name => array( attr, value )
+ $attributes = array(
+ 'PARSE_INT_ONLY' => array( NumberFormatter::PARSE_INT_ONLY, 1, 12345.123456 ),
+ 'GROUPING_USED' => array( NumberFormatter::GROUPING_USED, 0, 12345.123456 ),
+ 'DECIMAL_ALWAYS_SHOWN' => array( NumberFormatter::DECIMAL_ALWAYS_SHOWN, 1, 12345 ),
+ 'MAX_INTEGER_DIGITS' => array( NumberFormatter::MAX_INTEGER_DIGITS, 2, 12345.123456 ),
+ 'MIN_INTEGER_DIGITS' => array( NumberFormatter::MIN_INTEGER_DIGITS, 20, 12345.123456 ),
+ 'INTEGER_DIGITS' => array( NumberFormatter::INTEGER_DIGITS, 7, 12345.123456 ),
+ 'MAX_FRACTION_DIGITS' => array( NumberFormatter::MAX_FRACTION_DIGITS, 2, 12345.123456 ),
+ 'MIN_FRACTION_DIGITS' => array( NumberFormatter::MIN_FRACTION_DIGITS, 20, 12345.123456 ),
+ 'FRACTION_DIGITS' => array( NumberFormatter::FRACTION_DIGITS, 5, 12345.123456 ),
+ 'MULTIPLIER' => array( NumberFormatter::MULTIPLIER, 2, 12345.123456 ),
+ 'GROUPING_SIZE' => array( NumberFormatter::GROUPING_SIZE, 2, 12345.123456 ),
+ 'ROUNDING_MODE' => array( NumberFormatter::ROUNDING_MODE, 7, 12345.123456 ),
+ 'ROUNDING_INCREMENT' => array( NumberFormatter::ROUNDING_INCREMENT, (float)2, 12345.123456 ),
+ 'FORMAT_WIDTH' => array( NumberFormatter::FORMAT_WIDTH, 27, 12345.123456 ),
+ 'PADDING_POSITION' => array( NumberFormatter::PADDING_POSITION, 21, 12345.123456 ),
+ 'SECONDARY_GROUPING_SIZE' => array( NumberFormatter::SECONDARY_GROUPING_SIZE, 2, 12345.123456 ),
+ 'SIGNIFICANT_DIGITS_USED' => array( NumberFormatter::SIGNIFICANT_DIGITS_USED, 1, 12345.123456 ),
+ 'MIN_SIGNIFICANT_DIGITS' => array( NumberFormatter::MIN_SIGNIFICANT_DIGITS, 3, 1 ),
+ 'MAX_SIGNIFICANT_DIGITS' => array( NumberFormatter::MAX_SIGNIFICANT_DIGITS, 4, 12345.123456 ),
+ // 'LENIENT_PARSE' => array( NumberFormatter::LENIENT_PARSE, 2, 12345.123456 )
+ );
+
+ $res_str = '';
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+
+ foreach( $attributes as $attr_name => $args )
+ {
+ list( $attr, $new_val, $number ) = $args;
+ $res_str .= "\nAttribute $attr_name\n";
+
+ // Get original value of the attribute.
+ $orig_val = ut_nfmt_get_attribute( $fmt, $attr );
+
+ // Format the number using the original attribute value.
+ $rc = ut_nfmt_format( $fmt, $number );
+
+ $ps = ut_nfmt_parse( $fmt, $rc );
+
+ $res_str .= sprintf( "Old attribute value: %s ; Format result: %s ; Parse result: %s\n",
+ dump( $orig_val ),
+ dump( $rc ),
+ dump( $ps ) );
+
+ // Set new attribute value.
+ $rc = ut_nfmt_set_attribute( $fmt, $attr, $new_val );
+ if( $rc )
+ $res_str .= "Setting attribute: ok\n";
+ else
+ $res_str .= sprintf( "Setting attribute failed: %s\n", ut_nfmt_get_error_message( $fmt ) );
+
+ // Format the number using the new value.
+ $rc = ut_nfmt_format( $fmt, $number );
+
+ // Get current value of the attribute and check if it equals $new_val.
+ $attr_val_check = ut_nfmt_get_attribute( $fmt, $attr );
+ if( $attr_val_check !== $new_val )
+ $res_str .= "ERROR: New $attr_name attribute value has not been set correctly.\n";
+
+ $ps = ut_nfmt_parse( $fmt, $rc );
+
+ $res_str .= sprintf( "New attribute value: %s ; Format result: %s ; Parse result: %s\n",
+ dump( $new_val ),
+ dump( $rc ),
+ dump( $ps ) );
+
+
+ // Restore original attribute of the value
+ if( $attr != NumberFormatter::INTEGER_DIGITS && $attr != NumberFormatter::FRACTION_DIGITS
+ && $attr != NumberFormatter::FORMAT_WIDTH && $attr != NumberFormatter::SIGNIFICANT_DIGITS_USED )
+ ut_nfmt_set_attribute( $fmt, $attr, $orig_val );
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Attribute PARSE_INT_ONLY
+Old attribute value: 0 ; Format result: '12,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 1 ; Format result: '12,345.123' ; Parse result: 12345
+
+Attribute GROUPING_USED
+Old attribute value: 1 ; Format result: '12,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 0 ; Format result: '12345.123' ; Parse result: 12345.123
+
+Attribute DECIMAL_ALWAYS_SHOWN
+Old attribute value: 0 ; Format result: '12,345' ; Parse result: 12345
+Setting attribute: ok
+New attribute value: 1 ; Format result: '12,345.' ; Parse result: 12345
+
+Attribute MAX_INTEGER_DIGITS
+Old attribute value: 309 ; Format result: '12,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 2 ; Format result: '45.123' ; Parse result: 45.123
+
+Attribute MIN_INTEGER_DIGITS
+Old attribute value: 1 ; Format result: '12,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 20 ; Format result: '00,000,000,000,000,012,345.123' ; Parse result: 12345.123
+
+Attribute INTEGER_DIGITS
+Old attribute value: 1 ; Format result: '12,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 7 ; Format result: '0,012,345.123' ; Parse result: 12345.123
+
+Attribute MAX_FRACTION_DIGITS
+Old attribute value: 3 ; Format result: '0,012,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 2 ; Format result: '0,012,345.12' ; Parse result: 12345.12
+
+Attribute MIN_FRACTION_DIGITS
+Old attribute value: 0 ; Format result: '0,012,345.123' ; Parse result: 12345.123
+Setting attribute: ok
+New attribute value: 20 ; Format result: '0,012,345.12345600000000000000' ; Parse result: 12345.123456
+
+Attribute FRACTION_DIGITS
+Old attribute value: 0 ; Format result: '0,012,345.123456' ; Parse result: 12345.123456
+Setting attribute: ok
+New attribute value: 5 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+
+Attribute MULTIPLIER
+Old attribute value: 1 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 2 ; Format result: '0,024,690.24691' ; Parse result: 12345.123455
+
+Attribute GROUPING_SIZE
+Old attribute value: 3 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 2 ; Format result: '0,01,23,45.12346' ; Parse result: 12345.12346
+
+Attribute ROUNDING_MODE
+Old attribute value: 4 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 7 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+
+Attribute ROUNDING_INCREMENT
+Old attribute value: 0 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 2 ; Format result: '0,012,346.00000' ; Parse result: 12346
+
+Attribute FORMAT_WIDTH
+Old attribute value: 0 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 27 ; Format result: '************0,012,345.12346' ; Parse result: 12345.12346
+
+Attribute PADDING_POSITION
+Old attribute value: 0 ; Format result: '************0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 21 ; Format result: '0,012,345.12346' ; Parse result: 12345.12346
+
+Attribute SECONDARY_GROUPING_SIZE
+Old attribute value: 0 ; Format result: '************0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 2 ; Format result: '************00,12,345.12346' ; Parse result: 12345.12346
+
+Attribute SIGNIFICANT_DIGITS_USED
+Old attribute value: 0 ; Format result: '************0,012,345.12346' ; Parse result: 12345.12346
+Setting attribute: ok
+New attribute value: 1 ; Format result: '*******************12,345.1' ; Parse result: 12345.1
+
+Attribute MIN_SIGNIFICANT_DIGITS
+Old attribute value: 1 ; Format result: '**************************1' ; Parse result: 1
+Setting attribute: ok
+New attribute value: 3 ; Format result: '***********************1.00' ; Parse result: 1
+
+Attribute MAX_SIGNIFICANT_DIGITS
+Old attribute value: 6 ; Format result: '*******************12,345.1' ; Parse result: 12345.1
+Setting attribute: ok
+New attribute value: 4 ; Format result: '*********************12,350' ; Parse result: 12350
diff --git a/ext/intl/tests/formatter_get_set_pattern.phpt b/ext/intl/tests/formatter_get_set_pattern.phpt
new file mode 100755
index 0000000000..89f45a3c46
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_pattern.phpt
@@ -0,0 +1,52 @@
+--TEST--
+numfmt_get/set_pattern()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set pattern.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+ $test_value = 12345.123456;
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::PATTERN_DECIMAL );
+
+ // Get default patten.
+ $res_str .= "Default pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatting result: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ // Set a new pattern.
+ $res = ut_nfmt_set_pattern( $fmt, "0.0" );
+ if( $res === false )
+ $res_str .= ut_nfmt_get_error_message( $fmt ) . " (" . ut_nfmt_get_error_code( $fmt ) . ")\n";
+
+ // Check if the pattern has been changed.
+ $res = ut_nfmt_get_pattern( $fmt );
+ if( $res === false )
+ $res_str .= ut_nfmt_get_error_message( $fmt ) . " (" . ut_nfmt_get_error_code( $fmt ) . ")\n";
+ $res_str .= "New pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ ut_nfmt_set_pattern($fmt, str_repeat('@', 200));
+ $res_str .= "New pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Default pattern: '#.#####################################################################################################################################################################################################################################################################################################################'
+Formatting result: 12345.123456
+New pattern: '#0.0'
+Formatted number: 12345.1
+New pattern: '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
+Formatted number: 12345.123456000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
diff --git a/ext/intl/tests/formatter_get_set_symbol.phpt b/ext/intl/tests/formatter_get_set_symbol.phpt
new file mode 100755
index 0000000000..a1a430244f
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_symbol.phpt
@@ -0,0 +1,184 @@
+--TEST--
+numfmt_get/set_symbol()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set symbol.
+ */
+
+
+function ut_main()
+{
+ $longstr = str_repeat("blah", 10);
+ $symbols = array(
+ 'DECIMAL_SEPARATOR_SYMBOL' => array( NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, '_._', 12345.123456, NumberFormatter::DECIMAL ),
+ 'GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '_,_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PATTERN_SEPARATOR_SYMBOL' => array( NumberFormatter::PATTERN_SEPARATOR_SYMBOL, '_;_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PERCENT_SYMBOL' => array( NumberFormatter::PERCENT_SYMBOL, '_%_', 12345.123456, NumberFormatter::PERCENT ),
+ 'ZERO_DIGIT_SYMBOL' => array( NumberFormatter::ZERO_DIGIT_SYMBOL, '_ZD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'DIGIT_SYMBOL' => array( NumberFormatter::DIGIT_SYMBOL, '_DS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MINUS_SIGN_SYMBOL' => array( NumberFormatter::MINUS_SIGN_SYMBOL, '_-_', -12345.123456, NumberFormatter::DECIMAL ),
+ 'PLUS_SIGN_SYMBOL' => array( NumberFormatter::PLUS_SIGN_SYMBOL, '_+_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'CURRENCY_SYMBOL' => array( NumberFormatter::CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'INTL_CURRENCY_SYMBOL' => array( NumberFormatter::INTL_CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_SEPARATOR_SYMBOL, '_MS_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'EXPONENTIAL_SYMBOL' => array( NumberFormatter::EXPONENTIAL_SYMBOL, '_E_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'PERMILL_SYMBOL' => array( NumberFormatter::PERMILL_SYMBOL, '_PS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PAD_ESCAPE_SYMBOL' => array( NumberFormatter::PAD_ESCAPE_SYMBOL, '_PE_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'INFINITY_SYMBOL' => array( NumberFormatter::INFINITY_SYMBOL, '_IS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'NAN_SYMBOL' => array( NumberFormatter::NAN_SYMBOL, '_N_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'SIGNIFICANT_DIGIT_SYMBOL' => array( NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL, '_SD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, '_MG_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-2' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, "&nbsp;", 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-3' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, $longstr, 12345.123456, NumberFormatter::CURRENCY ),
+ );
+
+ $res_str = '';
+
+ foreach( $symbols as $symb_name => $data )
+ {
+ list( $symb, $new_val, $number, $attr ) = $data;
+
+ $fmt = ut_nfmt_create( 'en_US', $attr);
+
+ $res_str .= "\nSymbol '$symb_name'\n";
+
+ // Get original symbol value.
+ $orig_val = ut_nfmt_get_symbol( $fmt, $symb );
+ $res_str .= "Default symbol: [$orig_val]\n";
+
+ // Set a new symbol value.
+ $res_val = ut_nfmt_set_symbol( $fmt, $symb, $new_val );
+ if( !$res_val )
+ $res_str .= "set_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ // Get the symbol value back.
+ $new_val_check = ut_nfmt_get_symbol( $fmt, $symb );
+ if( !$new_val_check )
+ $res_str .= "get_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ $res_str .= "New symbol: [$new_val_check]\n";
+
+ // Check if the new value has been set.
+ if( $new_val_check !== $new_val )
+ $res_str .= "ERROR: New $symb_name symbol value has not been set correctly.\n";
+
+ // Format the number using the new value.
+ $s = ut_nfmt_format( $fmt, $number );
+ $res_str .= "A number formatted with the new symbol: $s\n";
+
+ // Restore attribute's symbol.
+ ut_nfmt_set_symbol( $fmt, $symb, $orig_val );
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Symbol 'DECIMAL_SEPARATOR_SYMBOL'
+Default symbol: [.]
+New symbol: [_._]
+A number formatted with the new symbol: 12,345_._123
+
+Symbol 'GROUPING_SEPARATOR_SYMBOL'
+Default symbol: [,]
+New symbol: [_,_]
+A number formatted with the new symbol: 12_,_345.123
+
+Symbol 'PATTERN_SEPARATOR_SYMBOL'
+Default symbol: [;]
+New symbol: [_;_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'PERCENT_SYMBOL'
+Default symbol: [%]
+New symbol: [_%_]
+A number formatted with the new symbol: 1,234,512_%_
+
+Symbol 'ZERO_DIGIT_SYMBOL'
+Default symbol: [0]
+New symbol: [_ZD_]
+A number formatted with the new symbol: `a,bcd.`ab
+
+Symbol 'DIGIT_SYMBOL'
+Default symbol: [#]
+New symbol: [_DS_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'MINUS_SIGN_SYMBOL'
+Default symbol: [-]
+New symbol: [_-_]
+A number formatted with the new symbol: _-_12,345.123
+
+Symbol 'PLUS_SIGN_SYMBOL'
+Default symbol: [+]
+New symbol: [_+_]
+A number formatted with the new symbol: 1.2345123456E4
+
+Symbol 'CURRENCY_SYMBOL'
+Default symbol: [$]
+New symbol: [_$_]
+A number formatted with the new symbol: _$_12,345.12
+
+Symbol 'INTL_CURRENCY_SYMBOL'
+Default symbol: [USD]
+New symbol: [_$_]
+A number formatted with the new symbol: $12,345.12
+
+Symbol 'MONETARY_SEPARATOR_SYMBOL'
+Default symbol: [.]
+New symbol: [_MS_]
+A number formatted with the new symbol: $12,345_MS_12
+
+Symbol 'EXPONENTIAL_SYMBOL'
+Default symbol: [E]
+New symbol: [_E_]
+A number formatted with the new symbol: 1.2345123456_E_4
+
+Symbol 'PERMILL_SYMBOL'
+Default symbol: [‰]
+New symbol: [_PS_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'PAD_ESCAPE_SYMBOL'
+Default symbol: [*]
+New symbol: [_PE_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'INFINITY_SYMBOL'
+Default symbol: [∞]
+New symbol: [_IS_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'NAN_SYMBOL'
+Default symbol: [NaN]
+New symbol: [_N_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'SIGNIFICANT_DIGIT_SYMBOL'
+Default symbol: [@]
+New symbol: [_SD_]
+A number formatted with the new symbol: 12,345.123
+
+Symbol 'MONETARY_GROUPING_SEPARATOR_SYMBOL'
+Default symbol: [,]
+New symbol: [_MG_]
+A number formatted with the new symbol: $12_MG_345.12
+
+Symbol 'MONETARY_GROUPING_SEPARATOR_SYMBOL-2'
+Default symbol: [,]
+New symbol: [&nbsp;]
+A number formatted with the new symbol: $12&nbsp;345.12
+
+Symbol 'MONETARY_GROUPING_SEPARATOR_SYMBOL-3'
+Default symbol: [,]
+New symbol: [blahblahblahblahblahblahblahblahblahblah]
+A number formatted with the new symbol: $12blahblahblahblahblahblahblahblahblahblah345.12
+
diff --git a/ext/intl/tests/formatter_get_set_text_attribute.phpt b/ext/intl/tests/formatter_get_set_text_attribute.phpt
new file mode 100755
index 0000000000..539ff52322
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_text_attribute.phpt
@@ -0,0 +1,121 @@
+--TEST--
+numfmt_get/set_text_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set text attribute.
+ */
+
+
+function ut_main()
+{
+ // Array with data for testing
+ $long_str = str_repeat('blah', 100);
+ $attributes = array(
+ 'POSITIVE_PREFIX' => array( NumberFormatter::POSITIVE_PREFIX, '_+_', 12345.1234 ),
+ 'POSITIVE_SUFFIX' => array( NumberFormatter::POSITIVE_SUFFIX, '_+_', 12345.1234 ),
+ 'NEGATIVE_PREFIX' => array( NumberFormatter::NEGATIVE_PREFIX, '_-_', -12345.1234 ),
+ 'NEGATIVE_SUFFIX' => array( NumberFormatter::NEGATIVE_SUFFIX, '_-_', -12345.1234 ),
+ 'PADDING_CHARACTER' => array( NumberFormatter::PADDING_CHARACTER, '^', 12345.1234 ),
+ 'POSITIVE_PREFIX-2' => array( NumberFormatter::POSITIVE_PREFIX, $long_str, 12345.1234 ),
+// 'CURRENCY_CODE' => array( NumberFormatter::CURRENCY_CODE, '_C_', 12345.1234 )
+// 'DEFAULT_RULESET' => array( NumberFormatter::DEFAULT_RULESET, '_DR_', 12345.1234 ),
+// 'PUBLIC_RULESETS' => array( NumberFormatter::PUBLIC_RULESETS, '_PR_', 12345.1234 )
+ );
+
+ $res_str = '';
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+
+ foreach( $attributes as $attr_name => $data )
+ {
+ list( $attr, $new_val, $test_number ) = $data;
+ $res_str .= "\nAttribute $attr_name\n";
+
+ if( $attr == NumberFormatter::PADDING_CHARACTER )
+ ut_nfmt_set_attribute( $fmt, NumberFormatter::FORMAT_WIDTH, 21 );
+
+ // Get default attribute's value
+ $def_val = ut_nfmt_get_text_attribute( $fmt, $attr );
+ if( $def_val === false )
+ $res_str .= "get_text_attribute() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ $res_str .= "Default value: [$def_val]\n";
+ $res_str .= "Formatting number with default value: " . ut_nfmt_format( $fmt, $test_number ) . "\n";
+
+ // Set new attribute's value and see if it works out.
+ $res_val = ut_nfmt_set_text_attribute( $fmt, $attr, $new_val );
+ if( !$res_val )
+ $res_str .= "set_text_attribute() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ // Get attribute value back.
+ $new_val_check = ut_nfmt_get_text_attribute( $fmt, $attr );
+ $res_str .= "New value: [$new_val_check]\n";
+ $res_str .= "Formatting number with new value: " . ut_nfmt_format( $fmt, $test_number ) . "\n";
+
+ // Check if the new value has been set.
+ if( $new_val !== $new_val_check )
+ $res_str .= "ERROR: New $attr_name symbol value has not been set correctly.\n";
+
+ // Restore attribute's value to default
+ ut_nfmt_set_text_attribute( $fmt, $attr, $def_val );
+
+ if( $attr == NumberFormatter::PADDING_CHARACTER )
+ ut_nfmt_set_attribute( $fmt, NumberFormatter::FORMAT_WIDTH, 0 );
+ }
+
+ //
+ $fmt = ut_nfmt_create( "uk_UA", NumberFormatter::CURRENCY );
+ $res_str .= sprintf( "\nCurrency ISO-code for locale 'uk_UA' is: %s\n",
+ ut_nfmt_get_text_attribute( $fmt, NumberFormatter::CURRENCY_CODE ) );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Attribute POSITIVE_PREFIX
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [_+_]
+Formatting number with new value: _+_12,345.123
+
+Attribute POSITIVE_SUFFIX
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [_+_]
+Formatting number with new value: 12,345.123_+_
+
+Attribute NEGATIVE_PREFIX
+Default value: [-]
+Formatting number with default value: -12,345.123
+New value: [_-_]
+Formatting number with new value: _-_12,345.123
+
+Attribute NEGATIVE_SUFFIX
+Default value: []
+Formatting number with default value: -12,345.123
+New value: [_-_]
+Formatting number with new value: -12,345.123_-_
+
+Attribute PADDING_CHARACTER
+Default value: [*]
+Formatting number with default value: ***********12,345.123
+New value: [^]
+Formatting number with new value: ^^^^^^^^^^^12,345.123
+
+Attribute POSITIVE_PREFIX-2
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah]
+Formatting number with new value: blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah12,345.123
+
+Currency ISO-code for locale 'uk_UA' is: UAH
+
+
diff --git a/ext/intl/tests/formatter_parse.phpt b/ext/intl/tests/formatter_parse.phpt
new file mode 100755
index 0000000000..2ab9cce3b6
--- /dev/null
+++ b/ext/intl/tests/formatter_parse.phpt
@@ -0,0 +1,42 @@
+--TEST--
+numfmt_parse()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Number parsing.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Test parsing float number.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $res_str .= ut_nfmt_parse( $fmt, "123E-3" ) . "\n";
+
+ // Test parsing float number as integer.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $res_str .= ut_nfmt_parse( $fmt, "1.23", NumberFormatter::TYPE_INT32 ) . "\n";
+
+ // Test specifying non-zero parsing start position.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $pos = 2;
+ $res_str .= ut_nfmt_parse( $fmt, "0.123 here", NumberFormatter::TYPE_DOUBLE, $pos ) . "\n";
+ $res_str .= "$pos\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+0.123
+1
+123
+5
diff --git a/ext/intl/tests/formatter_parse_currency.phpt b/ext/intl/tests/formatter_parse_currency.phpt
new file mode 100755
index 0000000000..4ebd1a4174
--- /dev/null
+++ b/ext/intl/tests/formatter_parse_currency.phpt
@@ -0,0 +1,37 @@
+--TEST--
+numfmt_parse_currency()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Currency parsing.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $pos = 0;
+ $currency = '';
+ $num = ut_nfmt_parse_currency( $fmt, '$9,988,776.65', $currency, $pos );
+ $res_str .= "$num $currency\n";
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $pos = 1;
+ $currency = '';
+ $num = ut_nfmt_parse_currency( $fmt, ' $123.45', $currency, $pos );
+ $res_str .= "$num $currency\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+9988776.65 USD
+123.45 USD
diff --git a/ext/intl/tests/grapheme.phpt b/ext/intl/tests/grapheme.phpt
new file mode 100755
index 0000000000..b5c0bdb833
--- /dev/null
+++ b/ext/intl/tests/grapheme.phpt
@@ -0,0 +1,1187 @@
+--TEST--
+grapheme()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test grapheme functions (procedural only)
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $char_a_diaeresis = "\xC3\xA4"; // 'LATIN SMALL LETTER A WITH DIAERESIS' (U+00E4)
+ $char_a_ring = "\xC3\xA5"; // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
+ $char_o_diaeresis = "\xC3\xB6"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
+ $char_O_diaeresis = "\xC3\x96"; // 'LATIN CAPITAL LETTER O WITH DIAERESIS' (U+00D6)
+
+ $char_angstrom_sign = "\xE2\x84\xAB"; // 'ANGSTROM SIGN' (U+212B)
+ $char_A_ring = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
+
+ $char_ohm_sign = "\xE2\x84\xA6"; // 'OHM SIGN' (U+2126)
+ $char_omega = "\xCE\xA9"; // 'GREEK CAPITAL LETTER OMEGA' (U+03A9)
+
+ $char_combining_ring_above = "\xCC\x8A"; // 'COMBINING RING ABOVE' (U+030A)
+
+ $char_fi_ligature = "\xEF\xAC\x81"; // 'LATIN SMALL LIGATURE FI' (U+FB01)
+
+ $char_long_s_dot = "\xE1\xBA\x9B"; // 'LATIN SMALL LETTER LONG S WITH DOT ABOVE' (U+1E9B)
+
+ // the word 'hindi' using Devanagari characters:
+ $hindi = "\xe0\xa4\xb9\xe0\xa4\xbf\xe0\xa4\xa8\xe0\xa5\x8d\xe0\xa4\xa6\xe0\xa5\x80";
+
+ $char_a_ring_nfd = "a\xCC\x8A";
+ $char_A_ring_nfd = "A\xCC\x8A";
+ $char_o_diaeresis_nfd = "o\xCC\x88";
+ $char_O_diaeresis_nfd = "O\xCC\x88";
+ $char_diaeresis = "\xCC\x88";
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_strlen($string) {}' . "\n\n";
+
+
+ $res_str .= "\"hindi\" in devanagari strlen " . grapheme_strlen($hindi) . "\n";
+ $res_str .= "\"ab\" + \"hindi\" + \"cde\" strlen " . grapheme_strlen('ab' . $hindi . 'cde') . "\n";
+ $res_str .= "\"\" strlen " . grapheme_strlen("") . "\n";
+ $res_str .= "char_a_ring_nfd strlen " . grapheme_strlen($char_a_ring_nfd) . "\n";
+ $res_str .= "char_a_ring_nfd + \"bc\" strlen " . grapheme_strlen($char_a_ring_nfd . 'bc') . "\n";
+ $res_str .= "\"abc\" strlen " . grapheme_strlen('abc') . "\n";
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_strpos($haystack, $needle, $offset = 0) {}' . "\n\n";
+
+ $tests = array(
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "o", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2 ),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
+ array( "abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "a", "false" ),
+ array( "abc", "d", "false" ),
+ array( "abc", "c", 2 ),
+ array( "abc", "b", 1 ),
+ array( "abc", "a", 0 ),
+ array( "abc", "a", 0, 0 ),
+ array( "abc", "a", 1, "false" ),
+ array( "ababc", "a", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "op", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "opq", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "abc", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, $char_o_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc", 2 ),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc", 1 ),
+ array( "abc", $char_a_ring_nfd . "bc", "false" ),
+ array( $char_a_ring_nfd . "bc", "abcdefg", "false" ),
+ array( "abc", "defghijklmnopq", "false" ),
+ array( "abc", "ab", 0 ),
+ array( "abc", "bc", 1 ),
+ array( "abc", "abc", 0 ),
+ array( "abc", "abcd", "false" ),
+ array( "abc", "ab", 0, 0 ),
+ array( "abc", "abc", 0, 0 ),
+ array( "abc", "abc", 1, "false" ),
+ array( "ababc", "ab", 1, 2 ),
+ array( "ababc", "abc", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "bc" . $char_a_ring_nfd, 2, 3 ),
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_strpos";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_strpos($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " from $test[2]";
+ $result = grapheme_strpos($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= $result;
+ }
+ $res_str .= " == " . $test[count($test)-1] . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_stripos($haystack, $needle, $offset = 0) {}' . "\n\n";
+
+ $tests = array(
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "O", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd, 2 ),
+ array( "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
+ array( "Abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "A", "false" ),
+ array( "abc", "D", "false" ),
+ array( "abC", "c", 2 ),
+ array( "abc", "B", 1 ),
+ array( "Abc", "a", 0 ),
+ array( "abc", "A", 0, 0 ),
+ array( "Abc", "a", 1, "false" ),
+ array( "ababc", "A", 1, 2 ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", "oP", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", "opQ", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "abc", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "bC" . $char_o_diaeresis_nfd, $char_O_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "Bc", $char_A_ring_nfd . "bc", 2 ),
+ array( "a" . $char_a_ring_nfd . "BC", $char_a_ring_nfd . "bc", 1 ),
+ array( "abc", $char_a_ring_nfd . "BC", "false" ),
+ array( $char_a_ring_nfd . "BC", "aBCdefg", "false" ),
+ array( "aBC", "Defghijklmnopq", "false" ),
+ array( "abC", "Ab", 0 ),
+ array( "aBC", "bc", 1 ),
+ array( "abC", "Abc", 0 ),
+ array( "abC", "aBcd", "false" ),
+ array( "ABc", "ab", 0, 0 ),
+ array( "aBc", "abC", 0, 0 ),
+ array( "abc", "aBc", 1, "false" ),
+ array( "ABabc", "AB", 1, 2 ),
+ array( "abaBc", "aBc", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_A_ring_nfd . "bC", "O" . $char_a_ring_nfd . "bC", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bC" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "Bc" . $char_a_ring_nfd, 2, 3 ),
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_stripos";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_stripos($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " from $test[2]";
+ $result = grapheme_stripos($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= $result;
+ }
+ $res_str .= " == " . $test[count($test)-1] . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_strrpos($haystack, $needle, $offset = 0) {}' . "\n\n";
+
+
+ $tests = array(
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "o", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2 ),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
+ array( "abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "a", "false" ),
+ array( "abc", "d", "false" ),
+ array( "abc", "c", 2 ),
+ array( "abc", "b", 1 ),
+ array( "abc", "a", 0 ),
+ array( "abc", "a", 0, 0 ),
+ array( "abc", "a", 1, "false" ),
+ array( "ababc", "a", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "op", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "opq", "opq", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "abc", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, $char_o_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc", 2 ),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc", 1 ),
+ array( "abc", $char_a_ring_nfd . "bc", "false" ),
+ array( $char_a_ring_nfd . "bc", "abcdefg", "false" ),
+ array( "abc", "defghijklmnopq", "false" ),
+ array( "abc", "ab", 0 ),
+ array( "abc", "bc", 1 ),
+ array( "abc", "abc", 0 ),
+ array( "abc", "abcd", "false" ),
+ array( "abc", "ab", 0, 0 ),
+ array( "abc", "abc", 0, 0 ),
+ array( "abc", "abc", 1, "false" ),
+ array( "ababc", "ab", 1, 2 ),
+ array( "ababc", "abc", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_a_ring_nfd . "bc", "o" . $char_a_ring_nfd . "bc", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_a_ring_nfd . "bc" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "bc" . $char_a_ring_nfd, 2, 3 ),
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_strrpos";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_strrpos($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " from $test[2]";
+ $result = grapheme_strrpos($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= $result;
+ }
+ $res_str .= " == " . $test[count($test)-1] . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_strripos($haystack, $needle, $offset = 0) {}' . "\n\n";
+
+ $tests = array(
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 2, 3 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "O", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd, 2 ),
+ array( "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, 1 ),
+ array( "Abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "A", "false" ),
+ array( "abc", "D", "false" ),
+ array( "abC", "c", 2 ),
+ array( "abc", "B", 1 ),
+ array( "Abc", "a", 0 ),
+ array( "abc", "A", 0, 0 ),
+ array( "Abc", "a", 1, "false" ),
+ array( "ababc", "A", 1, 2 ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", "oP", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", "opQ", 5 ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "abc", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "bC" . $char_o_diaeresis_nfd, $char_O_diaeresis_nfd . "bc" . $char_o_diaeresis_nfd, 4 ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "Bc", $char_A_ring_nfd . "bc", 2 ),
+ array( "a" . $char_a_ring_nfd . "BC", $char_a_ring_nfd . "bc", 1 ),
+ array( "abc", $char_a_ring_nfd . "BC", "false" ),
+ array( $char_a_ring_nfd . "BC", "aBCdefg", "false" ),
+ array( "aBC", "Defghijklmnopq", "false" ),
+ array( "abC", "Ab", 0 ),
+ array( "aBC", "bc", 1 ),
+ array( "abC", "Abc", 0 ),
+ array( "abC", "aBcd", "false" ),
+ array( "ABc", "ab", 0, 0 ),
+ array( "aBc", "abC", 0, 0 ),
+ array( "abc", "aBc", 1, "false" ),
+ array( "ABabc", "AB", 1, 2 ),
+ array( "abaBc", "aBc", 1, 2 ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o" . $char_A_ring_nfd . "bC", "O" . $char_a_ring_nfd . "bC", 2, 6 ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bC" . $char_a_ring_nfd . "def", $char_a_ring_nfd . "Bc" . $char_a_ring_nfd, 2, 3 ),
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_strripos";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_strripos($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " from $test[2]";
+ $result = grapheme_strripos($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= $result;
+ }
+ $res_str .= " == " . $test[count($test)-1] . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_substr($string, $start, $length = -1) {}' . "\n\n";
+
+ $tests = array(
+
+ array( "abc", 3, "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, 5, "false" ),
+ array( "ao" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", 2, $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O" ),
+ array( $char_o_diaeresis_nfd . $char_a_ring_nfd . "a" . $char_A_ring_nfd . "bc", 2, "a" . $char_A_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", 5, "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, 5, "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_O_diaeresis_nfd, 4, $char_O_diaeresis_nfd ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", 2, $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_A_ring_nfd . "bc", 1, $char_A_ring_nfd . "bc" ),
+ array( "Abc", -5, "false" ),
+ array( $char_a_ring_nfd . "bc", 3, "false" ),
+ array( "abc", 4, "false" ),
+ array( "abC", 2, "C" ),
+ array( "abc", 1, "bc" ),
+ array( "Abc", 1, 1, "b" ),
+ array( "abc", 0, 2, "ab" ),
+ array( "Abc", -4, 1, "false" ),
+ array( "ababc", 1, 2, "ba" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 5, "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 5, -1, "Op" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 5, -2, "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 5, -3, "" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 5, -4, "false" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -1, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Op" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -2, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -3, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -4, "a" . $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -5, "a" . $char_a_ring_nfd . "b" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -6, "a" . $char_a_ring_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -7, "a" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -8, "" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, -9, "false" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -7, $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -6, "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -5, "c" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -4, $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -3, "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -2, "pq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -1, "q" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -999, "false" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 8, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 7, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Op" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 6, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 5, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 4, "a" . $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 3, "a" . $char_a_ring_nfd . "b" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 2, "a" . $char_a_ring_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 1, "a" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, 0, "" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -999, "false" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -1, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Op" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -2, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -3, "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -4, "a" . $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -5, "a" . $char_a_ring_nfd . "b" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -6, "a" . $char_a_ring_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -7, "a" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -8, "" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", -8, -9, "false" ),
+
+ );
+
+ foreach( $tests as $test ) {
+ $arg0 = urlencode($test[0]);
+ $res_str .= "substring of \"$arg0\" from \"$test[1]\" - grapheme_substr";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_substr($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " with length $test[2]";
+ $result = grapheme_substr($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_strstr($haystack, $needle, $before_needle = FALSE) {}' . "\n\n";
+
+ $tests = array(
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "o", "o", "o" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "o", "false" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, $char_o_diaeresis_nfd, $char_o_diaeresis_nfd ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, $char_a_ring_nfd . "bc"),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, $char_a_ring_nfd . "bc"),
+ array( "abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "a", "false" ),
+ array( "abc", "d", "false" ),
+ array( "abc", "c", "c" ),
+ array( "abc", "b", "bc" ),
+ array( "abc", "a", "abc" ),
+ array( "abc", "ab", "abc" ),
+ array( "abc", "abc", "abc" ),
+ array( "abc", "bc", "bc" ),
+ array( "abc", "a", FALSE, "abc" ),
+ array( "abc", "a", TRUE, "" ),
+ array( "abc", "b", TRUE, "a" ),
+ array( "abc", "c", TRUE, "ab" ),
+ array( "ababc", "bab", TRUE, "a" ),
+ array( "ababc", "abc", TRUE, "ab" ),
+ array( "ababc", "abc", FALSE, "abc" ),
+
+ array( "ab" . $char_a_ring_nfd . "c", "d", "false" ),
+ array( "bc" . $char_a_ring_nfd . "a", "a", "a" ),
+ array( "a" . $char_a_ring_nfd . "bc", "b", "bc" ),
+ array( $char_a_ring_nfd . "bc", "a", "false" ),
+ array( $char_a_ring_nfd . "abc", "ab", "abc" ),
+ array( "abc" . $char_a_ring_nfd, "abc", "abc" . $char_a_ring_nfd),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc", $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, FALSE, $char_a_ring_nfd . "bc"),
+ array( "a" . $char_a_ring_nfd . "bc", "a", TRUE, "" ),
+ array( $char_a_ring_nfd . "abc", "b", TRUE, $char_a_ring_nfd . "a" ),
+ array( "ab" . $char_a_ring_nfd . "c", "c", TRUE, "ab" . $char_a_ring_nfd ),
+ array( "aba" . $char_a_ring_nfd . "bc", "ba" . $char_a_ring_nfd . "b", TRUE, "a" ),
+ array( "ababc" . $char_a_ring_nfd, "abc" . $char_a_ring_nfd, TRUE, "ab" ),
+ array( "abab" . $char_a_ring_nfd . "c", "ab" . $char_a_ring_nfd . "c", FALSE, "ab" . $char_a_ring_nfd . "c" ),
+
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_strstr";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_strstr($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " before flag is " . ( $test[2] ? "TRUE" : "FALSE" );
+ $result = grapheme_strstr($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_stristr($haystack, $needle, $before_needle = FALSE) {}' . "\n\n";
+
+ $tests = array(
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, $char_O_diaeresis_nfd, $char_o_diaeresis_nfd ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "O", "o", "O" ),
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd, "o", "false" ),
+ array( $char_o_diaeresis_nfd . "a" . $char_a_ring_nfd . "bc", $char_a_ring_nfd, $char_a_ring_nfd . "bc"),
+ array( "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd, $char_a_ring_nfd . "bc"),
+ array( "abc", $char_a_ring_nfd, "false" ),
+ array( $char_a_ring_nfd . "bc", "A", "false" ),
+ array( "abc", "d", "false" ),
+ array( "abc", "C", "c" ),
+ array( "aBc", "b", "Bc" ),
+ array( "abc", "A", "abc" ),
+ array( "abC", "ab", "abC" ),
+ array( "abc", "aBc", "abc" ),
+ array( "abC", "bc", "bC" ),
+ array( "abc", "A", FALSE, "abc" ),
+ array( "abc", "a", TRUE, "" ),
+ array( "aBc", "b", TRUE, "a" ),
+ array( "abc", "C", TRUE, "ab" ),
+ array( "aBabc", "bab", TRUE, "a" ),
+ array( "ababc", "aBc", TRUE, "ab" ),
+ array( "ababc", "abC", FALSE, "abc" ),
+
+ array( "ab" . $char_a_ring_nfd . "c", "d", "false" ),
+ array( "bc" . $char_a_ring_nfd . "A", "a", "A" ),
+ array( "a" . $char_a_ring_nfd . "bc", "B", "bc" ),
+ array( $char_A_ring_nfd . "bc", "a", "false" ),
+ array( $char_a_ring_nfd . "abc", "Ab", "abc" ),
+ array( "abc" . $char_A_ring_nfd, "abc", "abc" . $char_A_ring_nfd),
+ array( "a" . $char_a_ring_nfd . "bc", $char_A_ring_nfd . "bc", $char_a_ring_nfd . "bc" ),
+ array( "a" . $char_A_ring_nfd . "bc", $char_a_ring_nfd, FALSE, $char_A_ring_nfd . "bc" ),
+ array( "a" . $char_a_ring_nfd . "bc", "A", TRUE, "" ),
+ array( $char_a_ring_nfd . "aBc", "b", TRUE, $char_a_ring_nfd . "a" ),
+ array( "ab" . $char_a_ring_nfd . "c", "C", TRUE, "ab" . $char_a_ring_nfd ),
+ array( "aba" . $char_A_ring_nfd . "bc", "ba" . $char_a_ring_nfd . "b", TRUE, "a" ),
+ array( "ababc" . $char_a_ring_nfd, "aBc" . $char_A_ring_nfd, TRUE, "ab" ),
+ array( "abAB" . $char_A_ring_nfd . "c", "ab" . $char_a_ring_nfd . "c", FALSE, "AB" . $char_A_ring_nfd . "c" ),
+
+ );
+
+ foreach( $tests as $test ) {
+ $arg1 = urlencode($test[1]);
+ $arg0 = urlencode($test[0]);
+ $res_str .= "find \"$arg1\" in \"$arg0\" - grapheme_stristr";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_stristr($test[0], $test[1]);
+ }
+ else {
+ $res_str .= " before flag is " . ( $test[2] ? "TRUE" : "FALSE" );
+ $result = grapheme_stristr($test[0], $test[1], $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_COUNT, $start = 0[, $next])' . "\n\n";
+
+ $tests = array(
+ // haystack, count, [[offset], [next]], result
+ array( "abc", 3, "abc" ),
+ array( "abc", 2, "ab" ),
+ array( "abc", 1, "a" ),
+ array( "abc", 0, "" ),
+ array( "abc", 1, 0, "a" ),
+ array( "abc", 1, 1, "b" ),
+ array( "abc", 1, 2, "c" ),
+ array( "abc", 0, 2, "" ),
+
+ array( "abc", 3, 0, 3, "abc" ),
+ array( "abc", 2, 0, 2, "ab" ),
+ array( "abc", 1, 0, 1, "a" ),
+ array( "abc", 0, 0, 0, "" ),
+ array( "abc", 1, 0, 1, "a" ),
+ array( "abc", 1, 1, 2, "b" ),
+ array( "abc", 1, 2, 3, "c" ),
+ array( "abc", 0, 2, 2, "" ),
+
+
+ array( $char_a_ring_nfd . "bc", 3, $char_a_ring_nfd . "bc" ),
+ array( $char_a_ring_nfd . "bc", 2, $char_a_ring_nfd . "b" ),
+ array( $char_a_ring_nfd . "bc", 1, $char_a_ring_nfd . "" ),
+ array( $char_a_ring_nfd . "bc", 3, 0, 5, $char_a_ring_nfd . "bc" ),
+ array( $char_a_ring_nfd . "bc", 2, 0, 4, $char_a_ring_nfd . "b" ),
+ array( $char_a_ring_nfd . "bc", 1, 0, 3, $char_a_ring_nfd . "" ),
+ array( $char_a_ring_nfd . "bcde", 2, 3, 5, "bc" ),
+ array( $char_a_ring_nfd . "bcde", 2, 4, 6, "cd" ),
+ array( $char_a_ring_nfd . "bcde" . $char_a_ring_nfd . "f", 4, 5, 11, "de" . $char_a_ring_nfd . "f" ),
+
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, $char_a_ring_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 1, $char_a_ring_nfd . "" ),
+
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 1, 0, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 1, 2, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 1, 3, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 1, 4, $char_diaeresis),
+
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 0, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 2, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 3, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 4, $char_diaeresis . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 7, $char_diaeresis . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 8, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 10, $char_diaeresis),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 11, "false"),
+
+ );
+
+ $next = -1;
+ foreach( $tests as $test ) {
+ $arg0 = urlencode($test[0]);
+ $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extract";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_extract($test[0], $test[1]);
+ }
+ elseif ( 4 == count ( $test ) ) {
+ $res_str .= " starting at byte position $test[2]";
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_COUNT, $test[2]);
+ }
+ else {
+ $res_str .= " starting at byte position $test[2] with \$next";
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_COUNT, $test[2], $next);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]);
+ if ( 5 == count ( $test ) ) {
+ $res_str .= " \$next=$next == $test[3] ";
+ if ( $next != $test[3] ) {
+ $res_str .= "***FAILED***";
+ }
+ }
+ $res_str .= "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_MAXBYTES, $start = 0)' . "\n\n";
+
+ $tests = array(
+ array( "abc", 3, "abc" ),
+ array( "abc", 2, "ab" ),
+ array( "abc", 1, "a" ),
+ array( "abc", 0, "" ),
+ array( $char_a_ring_nfd . "bc", 5, $char_a_ring_nfd . "bc" ),
+ array( $char_a_ring_nfd . "bc", 4, $char_a_ring_nfd . "b" ),
+ array( $char_a_ring_nfd . "bc", 1, "" ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 9, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 10, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 11, $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 6, $char_a_ring_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 3, $char_a_ring_nfd . "" ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 4, $char_a_ring_nfd . "" ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 5, $char_a_ring_nfd . "" ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 6, $char_a_ring_nfd . $char_o_diaeresis_nfd ),
+ array( $char_a_ring_nfd . $char_o_diaeresis_nfd . "c", 7, $char_a_ring_nfd . $char_o_diaeresis_nfd . "c" ),
+
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, 0, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, 2, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, 3, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, 4, $char_diaeresis),
+
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 6, 0, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 6, 2, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 6, 3, $char_o_diaeresis_nfd . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 5, 4, $char_diaeresis . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 5, 7, $char_diaeresis . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 3, 8, $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 10, $char_diaeresis),
+ array( $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd . $char_o_diaeresis_nfd, 2, 11, "false"),
+
+ );
+
+ foreach( $tests as $test ) {
+ $arg0 = urlencode($test[0]);
+ $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_MAXBYTES);
+ }
+ else {
+ $res_str .= " starting at byte position $test[2]";
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_MAXBYTES, $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+ $res_str .= "\n" . 'function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_MAXCHARS, $start = 0)' . "\n\n";
+
+ $tests = array(
+ array( "abc", 3, "abc" ),
+ array( "abc", 2, "ab" ),
+ array( "abc", 1, "a" ),
+ array( "abc", 0, "" ),
+ array( "abc" . $char_o_diaeresis_nfd, 0, "" ),
+ array( "abc" . $char_o_diaeresis_nfd, 1, "a" ),
+ array( "abc" . $char_o_diaeresis_nfd, 2, "ab" ),
+ array( "abc" . $char_o_diaeresis_nfd, 3, "abc" ),
+ array( "abc" . $char_o_diaeresis_nfd, 4, "abc" ),
+ array( "abc" . $char_o_diaeresis_nfd, 5, "abc" . $char_o_diaeresis_nfd),
+ array( "abc" . $char_o_diaeresis_nfd, 6, "abc" . $char_o_diaeresis_nfd),
+ array( $char_o_diaeresis_nfd . "abc", 0, "" ),
+ array( $char_o_diaeresis_nfd . "abc", 1, "" ),
+ array( $char_o_diaeresis_nfd . "abc", 2, $char_o_diaeresis_nfd ),
+ array( $char_o_diaeresis_nfd . "abc", 3, $char_o_diaeresis_nfd . "a" ),
+ array( $char_o_diaeresis_nfd . "abc", 4, $char_o_diaeresis_nfd . "ab" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 5, $char_o_diaeresis_nfd . "abc" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 6, $char_o_diaeresis_nfd . "abc" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 7, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ),
+
+ array( "abc", 3, 0, "abc" ),
+ array( "abc", 2, 1, "bc" ),
+ array( "abc", 1, 2, "c" ),
+ array( "abc", 0, 3, "false" ),
+ array( "abc", 1, 3, "false" ),
+ array( "abc", 1, 999, "false" ),
+ array( $char_o_diaeresis_nfd . "abc", 1, 6, "false" ),
+ array( $char_o_diaeresis_nfd . "abc", 1, 999, "false" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 0, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 1, $char_diaeresis . "abc" . $char_a_ring_nfd . "xy" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 2, "abc" . $char_a_ring_nfd . "xyz" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 3, "abc" . $char_a_ring_nfd . "xyz" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 4, "bc" . $char_a_ring_nfd . "xyz" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 5, "c" . $char_a_ring_nfd . "xyz" ),
+ array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 6, $char_a_ring_nfd . "xyz" ),
+
+ );
+
+ foreach( $tests as $test ) {
+ $arg0 = urlencode($test[0]);
+ $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS";
+ if ( 3 == count( $test ) ) {
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_MAXCHARS);
+ }
+ else {
+ $res_str .= " starting at byte position $test[2]";
+ $result = grapheme_extract($test[0], $test[1], GRAPHEME_EXTR_MAXCHARS, $test[2]);
+ }
+ $res_str .= " = ";
+ if ( $result === false ) {
+ $res_str .= 'false';
+ }
+ else {
+ $res_str .= urlencode($result);
+ }
+ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n";
+ }
+
+
+ //=====================================================================================
+
+ return $res_str;
+}
+
+echo ut_main();
+
+function check_result($result, $expected) {
+
+ if ( $result === false ) {
+ $result = 'false';
+ }
+
+ if ( strcmp($result, $expected) != 0 ) {
+ return " **FAILED** ";
+ }
+
+ return "";
+}
+
+?>
+--EXPECT--
+
+function grapheme_strlen($string) {}
+
+"hindi" in devanagari strlen 5
+"ab" + "hindi" + "cde" strlen 10
+"" strlen 0
+char_a_ring_nfd strlen 1
+char_a_ring_nfd + "bc" strlen 3
+"abc" strlen 3
+
+function grapheme_strpos($haystack, $needle, $offset = 0) {}
+
+find "o" in "aa%CC%8Abco%CC%88o" - grapheme_strpos = 5 == 5
+find "o" in "aa%CC%8Abco%CC%88" - grapheme_strpos = false == false
+find "o%CC%88" in "aa%CC%8Abco%CC%88" - grapheme_strpos = 4 == 4
+find "a%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_strpos = 2 == 2
+find "a%CC%8A" in "aa%CC%8Abc" - grapheme_strpos = 1 == 1
+find "a%CC%8A" in "abc" - grapheme_strpos = false == false
+find "a" in "a%CC%8Abc" - grapheme_strpos = false == false
+find "d" in "abc" - grapheme_strpos = false == false
+find "c" in "abc" - grapheme_strpos = 2 == 2
+find "b" in "abc" - grapheme_strpos = 1 == 1
+find "a" in "abc" - grapheme_strpos = 0 == 0
+find "a" in "abc" - grapheme_strpos from 0 = 0 == 0
+find "a" in "abc" - grapheme_strpos from 1 = false == false
+find "a" in "ababc" - grapheme_strpos from 1 = 2 == 2
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strpos from 2 = 6 == 6
+find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strpos from 2 = 3 == 3
+find "op" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
+find "opq" in "aa%CC%8Abco%CC%88opq" - grapheme_strpos = 5 == 5
+find "abc" in "aa%CC%8Abco%CC%88" - grapheme_strpos = false == false
+find "o%CC%88bco%CC%88" in "aa%CC%8Abco%CC%88bco%CC%88" - grapheme_strpos = 4 == 4
+find "a%CC%8Abc" in "o%CC%88aa%CC%8Abc" - grapheme_strpos = 2 == 2
+find "a%CC%8Abc" in "aa%CC%8Abc" - grapheme_strpos = 1 == 1
+find "a%CC%8Abc" in "abc" - grapheme_strpos = false == false
+find "abcdefg" in "a%CC%8Abc" - grapheme_strpos = false == false
+find "defghijklmnopq" in "abc" - grapheme_strpos = false == false
+find "ab" in "abc" - grapheme_strpos = 0 == 0
+find "bc" in "abc" - grapheme_strpos = 1 == 1
+find "abc" in "abc" - grapheme_strpos = 0 == 0
+find "abcd" in "abc" - grapheme_strpos = false == false
+find "ab" in "abc" - grapheme_strpos from 0 = 0 == 0
+find "abc" in "abc" - grapheme_strpos from 0 = 0 == 0
+find "abc" in "abc" - grapheme_strpos from 1 = false == false
+find "ab" in "ababc" - grapheme_strpos from 1 = 2 == 2
+find "abc" in "ababc" - grapheme_strpos from 1 = 2 == 2
+find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strpos from 2 = 6 == 6
+find "a%CC%8Abca%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abca%CC%8Adef" - grapheme_strpos from 2 = 3 == 3
+
+function grapheme_stripos($haystack, $needle, $offset = 0) {}
+
+find "o" in "aoa%CC%8Abco%CC%88O" - grapheme_stripos from 2 = 6 == 6
+find "a%CC%8A" in "o%CC%88a%CC%8AaA%CC%8Abc" - grapheme_stripos from 2 = 3 == 3
+find "o" in "aa%CC%8Abco%CC%88O" - grapheme_stripos = 5 == 5
+find "O" in "aa%CC%8Abco%CC%88" - grapheme_stripos = false == false
+find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_stripos = 4 == 4
+find "A%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_stripos = 2 == 2
+find "a%CC%8A" in "aA%CC%8Abc" - grapheme_stripos = 1 == 1
+find "a%CC%8A" in "Abc" - grapheme_stripos = false == false
+find "A" in "a%CC%8Abc" - grapheme_stripos = false == false
+find "D" in "abc" - grapheme_stripos = false == false
+find "c" in "abC" - grapheme_stripos = 2 == 2
+find "B" in "abc" - grapheme_stripos = 1 == 1
+find "a" in "Abc" - grapheme_stripos = 0 == 0
+find "A" in "abc" - grapheme_stripos from 0 = 0 == 0
+find "a" in "Abc" - grapheme_stripos from 1 = false == false
+find "A" in "ababc" - grapheme_stripos from 1 = 2 == 2
+find "oP" in "aa%CC%8Abco%CC%88Opq" - grapheme_stripos = 5 == 5
+find "opQ" in "aa%CC%8Abco%CC%88Opq" - grapheme_stripos = 5 == 5
+find "abc" in "aa%CC%8Abco%CC%88" - grapheme_stripos = false == false
+find "O%CC%88bco%CC%88" in "aa%CC%8Abco%CC%88bCo%CC%88" - grapheme_stripos = 4 == 4
+find "A%CC%8Abc" in "o%CC%88aa%CC%8ABc" - grapheme_stripos = 2 == 2
+find "a%CC%8Abc" in "aa%CC%8ABC" - grapheme_stripos = 1 == 1
+find "a%CC%8ABC" in "abc" - grapheme_stripos = false == false
+find "aBCdefg" in "a%CC%8ABC" - grapheme_stripos = false == false
+find "Defghijklmnopq" in "aBC" - grapheme_stripos = false == false
+find "Ab" in "abC" - grapheme_stripos = 0 == 0
+find "bc" in "aBC" - grapheme_stripos = 1 == 1
+find "Abc" in "abC" - grapheme_stripos = 0 == 0
+find "aBcd" in "abC" - grapheme_stripos = false == false
+find "ab" in "ABc" - grapheme_stripos from 0 = 0 == 0
+find "abC" in "aBc" - grapheme_stripos from 0 = 0 == 0
+find "aBc" in "abc" - grapheme_stripos from 1 = false == false
+find "AB" in "ABabc" - grapheme_stripos from 1 = 2 == 2
+find "aBc" in "abaBc" - grapheme_stripos from 1 = 2 == 2
+find "Oa%CC%8AbC" in "aoa%CC%8Abco%CC%88oA%CC%8AbC" - grapheme_stripos from 2 = 6 == 6
+find "a%CC%8ABca%CC%8A" in "o%CC%88a%CC%8AaA%CC%8AbCa%CC%8Adef" - grapheme_stripos from 2 = 3 == 3
+
+function grapheme_strrpos($haystack, $needle, $offset = 0) {}
+
+find "o" in "aa%CC%8Abco%CC%88o" - grapheme_strrpos = 5 == 5
+find "o" in "aa%CC%8Abco%CC%88" - grapheme_strrpos = false == false
+find "o%CC%88" in "aa%CC%8Abco%CC%88" - grapheme_strrpos = 4 == 4
+find "a%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_strrpos = 2 == 2
+find "a%CC%8A" in "aa%CC%8Abc" - grapheme_strrpos = 1 == 1
+find "a%CC%8A" in "abc" - grapheme_strrpos = false == false
+find "a" in "a%CC%8Abc" - grapheme_strrpos = false == false
+find "d" in "abc" - grapheme_strrpos = false == false
+find "c" in "abc" - grapheme_strrpos = 2 == 2
+find "b" in "abc" - grapheme_strrpos = 1 == 1
+find "a" in "abc" - grapheme_strrpos = 0 == 0
+find "a" in "abc" - grapheme_strrpos from 0 = 0 == 0
+find "a" in "abc" - grapheme_strrpos from 1 = false == false
+find "a" in "ababc" - grapheme_strrpos from 1 = 2 == 2
+find "o" in "aoa%CC%8Abco%CC%88o" - grapheme_strrpos from 2 = 6 == 6
+find "a%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abc" - grapheme_strrpos from 2 = 3 == 3
+find "op" in "aa%CC%8Abco%CC%88opq" - grapheme_strrpos = 5 == 5
+find "opq" in "aa%CC%8Abco%CC%88opq" - grapheme_strrpos = 5 == 5
+find "abc" in "aa%CC%8Abco%CC%88" - grapheme_strrpos = false == false
+find "o%CC%88bco%CC%88" in "aa%CC%8Abco%CC%88bco%CC%88" - grapheme_strrpos = 4 == 4
+find "a%CC%8Abc" in "o%CC%88aa%CC%8Abc" - grapheme_strrpos = 2 == 2
+find "a%CC%8Abc" in "aa%CC%8Abc" - grapheme_strrpos = 1 == 1
+find "a%CC%8Abc" in "abc" - grapheme_strrpos = false == false
+find "abcdefg" in "a%CC%8Abc" - grapheme_strrpos = false == false
+find "defghijklmnopq" in "abc" - grapheme_strrpos = false == false
+find "ab" in "abc" - grapheme_strrpos = 0 == 0
+find "bc" in "abc" - grapheme_strrpos = 1 == 1
+find "abc" in "abc" - grapheme_strrpos = 0 == 0
+find "abcd" in "abc" - grapheme_strrpos = false == false
+find "ab" in "abc" - grapheme_strrpos from 0 = 0 == 0
+find "abc" in "abc" - grapheme_strrpos from 0 = 0 == 0
+find "abc" in "abc" - grapheme_strrpos from 1 = false == false
+find "ab" in "ababc" - grapheme_strrpos from 1 = 2 == 2
+find "abc" in "ababc" - grapheme_strrpos from 1 = 2 == 2
+find "oa%CC%8Abc" in "aoa%CC%8Abco%CC%88oa%CC%8Abc" - grapheme_strrpos from 2 = 6 == 6
+find "a%CC%8Abca%CC%8A" in "o%CC%88a%CC%8Aaa%CC%8Abca%CC%8Adef" - grapheme_strrpos from 2 = 3 == 3
+
+function grapheme_strripos($haystack, $needle, $offset = 0) {}
+
+find "o" in "aoa%CC%8Abco%CC%88O" - grapheme_strripos from 2 = 6 == 6
+find "a%CC%8A" in "o%CC%88a%CC%8AaA%CC%8Abc" - grapheme_strripos from 2 = 3 == 3
+find "o" in "aa%CC%8Abco%CC%88O" - grapheme_strripos = 5 == 5
+find "O" in "aa%CC%8Abco%CC%88" - grapheme_strripos = false == false
+find "o%CC%88" in "aa%CC%8AbcO%CC%88" - grapheme_strripos = 4 == 4
+find "A%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_strripos = 2 == 2
+find "a%CC%8A" in "aA%CC%8Abc" - grapheme_strripos = 1 == 1
+find "a%CC%8A" in "Abc" - grapheme_strripos = false == false
+find "A" in "a%CC%8Abc" - grapheme_strripos = false == false
+find "D" in "abc" - grapheme_strripos = false == false
+find "c" in "abC" - grapheme_strripos = 2 == 2
+find "B" in "abc" - grapheme_strripos = 1 == 1
+find "a" in "Abc" - grapheme_strripos = 0 == 0
+find "A" in "abc" - grapheme_strripos from 0 = 0 == 0
+find "a" in "Abc" - grapheme_strripos from 1 = false == false
+find "A" in "ababc" - grapheme_strripos from 1 = 2 == 2
+find "oP" in "aa%CC%8Abco%CC%88Opq" - grapheme_strripos = 5 == 5
+find "opQ" in "aa%CC%8Abco%CC%88Opq" - grapheme_strripos = 5 == 5
+find "abc" in "aa%CC%8Abco%CC%88" - grapheme_strripos = false == false
+find "O%CC%88bco%CC%88" in "aa%CC%8Abco%CC%88bCo%CC%88" - grapheme_strripos = 4 == 4
+find "A%CC%8Abc" in "o%CC%88aa%CC%8ABc" - grapheme_strripos = 2 == 2
+find "a%CC%8Abc" in "aa%CC%8ABC" - grapheme_strripos = 1 == 1
+find "a%CC%8ABC" in "abc" - grapheme_strripos = false == false
+find "aBCdefg" in "a%CC%8ABC" - grapheme_strripos = false == false
+find "Defghijklmnopq" in "aBC" - grapheme_strripos = false == false
+find "Ab" in "abC" - grapheme_strripos = 0 == 0
+find "bc" in "aBC" - grapheme_strripos = 1 == 1
+find "Abc" in "abC" - grapheme_strripos = 0 == 0
+find "aBcd" in "abC" - grapheme_strripos = false == false
+find "ab" in "ABc" - grapheme_strripos from 0 = 0 == 0
+find "abC" in "aBc" - grapheme_strripos from 0 = 0 == 0
+find "aBc" in "abc" - grapheme_strripos from 1 = false == false
+find "AB" in "ABabc" - grapheme_strripos from 1 = 2 == 2
+find "aBc" in "abaBc" - grapheme_strripos from 1 = 2 == 2
+find "Oa%CC%8AbC" in "aoa%CC%8Abco%CC%88oA%CC%8AbC" - grapheme_strripos from 2 = 6 == 6
+find "a%CC%8ABca%CC%8A" in "o%CC%88a%CC%8AaA%CC%8AbCa%CC%8Adef" - grapheme_strripos from 2 = 3 == 3
+
+function grapheme_substr($string, $start, $length = -1) {}
+
+substring of "abc" from "3" - grapheme_substr = false == false
+substring of "aa%CC%8Abco%CC%88" from "5" - grapheme_substr = false == false
+substring of "aoa%CC%8Abco%CC%88O" from "2" - grapheme_substr = a%CC%8Abco%CC%88O == a%CC%8Abco%CC%88O
+substring of "o%CC%88a%CC%8AaA%CC%8Abc" from "2" - grapheme_substr = aA%CC%8Abc == aA%CC%8Abc
+substring of "aa%CC%8Abco%CC%88O" from "5" - grapheme_substr = O == O
+substring of "aa%CC%8Abco%CC%88" from "5" - grapheme_substr = false == false
+substring of "aa%CC%8AbcO%CC%88" from "4" - grapheme_substr = O%CC%88 == O%CC%88
+substring of "o%CC%88aa%CC%8Abc" from "2" - grapheme_substr = a%CC%8Abc == a%CC%8Abc
+substring of "aA%CC%8Abc" from "1" - grapheme_substr = A%CC%8Abc == A%CC%8Abc
+substring of "Abc" from "-5" - grapheme_substr = false == false
+substring of "a%CC%8Abc" from "3" - grapheme_substr = false == false
+substring of "abc" from "4" - grapheme_substr = false == false
+substring of "abC" from "2" - grapheme_substr = C == C
+substring of "abc" from "1" - grapheme_substr = bc == bc
+substring of "Abc" from "1" - grapheme_substr with length 1 = b == b
+substring of "abc" from "0" - grapheme_substr with length 2 = ab == ab
+substring of "Abc" from "-4" - grapheme_substr with length 1 = false == false
+substring of "ababc" from "1" - grapheme_substr with length 2 = ba == ba
+substring of "aa%CC%8Abco%CC%88Opq" from "5" - grapheme_substr = Opq == Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "5" - grapheme_substr with length -1 = Op == Op
+substring of "aa%CC%8Abco%CC%88Opq" from "5" - grapheme_substr with length -2 = O == O
+substring of "aa%CC%8Abco%CC%88Opq" from "5" - grapheme_substr with length -3 = ==
+substring of "aa%CC%8Abco%CC%88Opq" from "5" - grapheme_substr with length -4 = false == false
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr = aa%CC%8Abco%CC%88Opq == aa%CC%8Abco%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -1 = aa%CC%8Abco%CC%88Op == aa%CC%8Abco%CC%88Op
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -2 = aa%CC%8Abco%CC%88O == aa%CC%8Abco%CC%88O
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -3 = aa%CC%8Abco%CC%88 == aa%CC%8Abco%CC%88
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -4 = aa%CC%8Abc == aa%CC%8Abc
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -5 = aa%CC%8Ab == aa%CC%8Ab
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -6 = aa%CC%8A == aa%CC%8A
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -7 = a == a
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -8 = ==
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length -9 = false == false
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr = aa%CC%8Abco%CC%88Opq == aa%CC%8Abco%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-7" - grapheme_substr = a%CC%8Abco%CC%88Opq == a%CC%8Abco%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-6" - grapheme_substr = bco%CC%88Opq == bco%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-5" - grapheme_substr = co%CC%88Opq == co%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-4" - grapheme_substr = o%CC%88Opq == o%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-3" - grapheme_substr = Opq == Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-2" - grapheme_substr = pq == pq
+substring of "aa%CC%8Abco%CC%88Opq" from "-1" - grapheme_substr = q == q
+substring of "aa%CC%8Abco%CC%88Opq" from "-999" - grapheme_substr = false == false
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 8 = aa%CC%8Abco%CC%88Opq == aa%CC%8Abco%CC%88Opq
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 7 = aa%CC%8Abco%CC%88Op == aa%CC%8Abco%CC%88Op
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 6 = aa%CC%8Abco%CC%88O == aa%CC%8Abco%CC%88O
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 5 = aa%CC%8Abco%CC%88 == aa%CC%8Abco%CC%88
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 4 = aa%CC%8Abc == aa%CC%8Abc
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 3 = aa%CC%8Ab == aa%CC%8Ab
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 2 = aa%CC%8A == aa%CC%8A
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 1 = a == a
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length 0 = ==
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -999 = false == false
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -1 = aa%CC%8Abco%CC%88Op == aa%CC%8Abco%CC%88Op
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -2 = aa%CC%8Abco%CC%88O == aa%CC%8Abco%CC%88O
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -3 = aa%CC%8Abco%CC%88 == aa%CC%8Abco%CC%88
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -4 = aa%CC%8Abc == aa%CC%8Abc
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -5 = aa%CC%8Ab == aa%CC%8Ab
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -6 = aa%CC%8A == aa%CC%8A
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -7 = a == a
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -8 = ==
+substring of "aa%CC%8Abco%CC%88Opq" from "-8" - grapheme_substr with length -9 = false == false
+
+function grapheme_strstr($haystack, $needle, $before_needle = FALSE) {}
+
+find "o" in "aa%CC%8Abco%CC%88o" - grapheme_strstr = o == o
+find "o" in "aa%CC%8Abco%CC%88" - grapheme_strstr = false == false
+find "o%CC%88" in "aa%CC%8Abco%CC%88" - grapheme_strstr = o%CC%88 == o%CC%88
+find "a%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_strstr = a%CC%8Abc == a%CC%8Abc
+find "a%CC%8A" in "aa%CC%8Abc" - grapheme_strstr = a%CC%8Abc == a%CC%8Abc
+find "a%CC%8A" in "abc" - grapheme_strstr = false == false
+find "a" in "a%CC%8Abc" - grapheme_strstr = false == false
+find "d" in "abc" - grapheme_strstr = false == false
+find "c" in "abc" - grapheme_strstr = c == c
+find "b" in "abc" - grapheme_strstr = bc == bc
+find "a" in "abc" - grapheme_strstr = abc == abc
+find "ab" in "abc" - grapheme_strstr = abc == abc
+find "abc" in "abc" - grapheme_strstr = abc == abc
+find "bc" in "abc" - grapheme_strstr = bc == bc
+find "a" in "abc" - grapheme_strstr before flag is FALSE = abc == abc
+find "a" in "abc" - grapheme_strstr before flag is TRUE = ==
+find "b" in "abc" - grapheme_strstr before flag is TRUE = a == a
+find "c" in "abc" - grapheme_strstr before flag is TRUE = ab == ab
+find "bab" in "ababc" - grapheme_strstr before flag is TRUE = a == a
+find "abc" in "ababc" - grapheme_strstr before flag is TRUE = ab == ab
+find "abc" in "ababc" - grapheme_strstr before flag is FALSE = abc == abc
+find "d" in "aba%CC%8Ac" - grapheme_strstr = false == false
+find "a" in "bca%CC%8Aa" - grapheme_strstr = a == a
+find "b" in "aa%CC%8Abc" - grapheme_strstr = bc == bc
+find "a" in "a%CC%8Abc" - grapheme_strstr = false == false
+find "ab" in "a%CC%8Aabc" - grapheme_strstr = abc == abc
+find "abc" in "abca%CC%8A" - grapheme_strstr = abca%CC%8A == abca%CC%8A
+find "a%CC%8Abc" in "aa%CC%8Abc" - grapheme_strstr = a%CC%8Abc == a%CC%8Abc
+find "a%CC%8A" in "aa%CC%8Abc" - grapheme_strstr before flag is FALSE = a%CC%8Abc == a%CC%8Abc
+find "a" in "aa%CC%8Abc" - grapheme_strstr before flag is TRUE = ==
+find "b" in "a%CC%8Aabc" - grapheme_strstr before flag is TRUE = a%CC%8Aa == a%CC%8Aa
+find "c" in "aba%CC%8Ac" - grapheme_strstr before flag is TRUE = aba%CC%8A == aba%CC%8A
+find "baa%CC%8Ab" in "abaa%CC%8Abc" - grapheme_strstr before flag is TRUE = a == a
+find "abca%CC%8A" in "ababca%CC%8A" - grapheme_strstr before flag is TRUE = ab == ab
+find "aba%CC%8Ac" in "ababa%CC%8Ac" - grapheme_strstr before flag is FALSE = aba%CC%8Ac == aba%CC%8Ac
+
+function grapheme_stristr($haystack, $needle, $before_needle = FALSE) {}
+
+find "O%CC%88" in "aa%CC%8Abco%CC%88" - grapheme_stristr = o%CC%88 == o%CC%88
+find "o" in "aa%CC%8Abco%CC%88O" - grapheme_stristr = O == O
+find "o" in "aa%CC%8Abco%CC%88" - grapheme_stristr = false == false
+find "a%CC%8A" in "o%CC%88aa%CC%8Abc" - grapheme_stristr = a%CC%8Abc == a%CC%8Abc
+find "A%CC%8A" in "aa%CC%8Abc" - grapheme_stristr = a%CC%8Abc == a%CC%8Abc
+find "a%CC%8A" in "abc" - grapheme_stristr = false == false
+find "A" in "a%CC%8Abc" - grapheme_stristr = false == false
+find "d" in "abc" - grapheme_stristr = false == false
+find "C" in "abc" - grapheme_stristr = c == c
+find "b" in "aBc" - grapheme_stristr = Bc == Bc
+find "A" in "abc" - grapheme_stristr = abc == abc
+find "ab" in "abC" - grapheme_stristr = abC == abC
+find "aBc" in "abc" - grapheme_stristr = abc == abc
+find "bc" in "abC" - grapheme_stristr = bC == bC
+find "A" in "abc" - grapheme_stristr before flag is FALSE = abc == abc
+find "a" in "abc" - grapheme_stristr before flag is TRUE = ==
+find "b" in "aBc" - grapheme_stristr before flag is TRUE = a == a
+find "C" in "abc" - grapheme_stristr before flag is TRUE = ab == ab
+find "bab" in "aBabc" - grapheme_stristr before flag is TRUE = a == a
+find "aBc" in "ababc" - grapheme_stristr before flag is TRUE = ab == ab
+find "abC" in "ababc" - grapheme_stristr before flag is FALSE = abc == abc
+find "d" in "aba%CC%8Ac" - grapheme_stristr = false == false
+find "a" in "bca%CC%8AA" - grapheme_stristr = A == A
+find "B" in "aa%CC%8Abc" - grapheme_stristr = bc == bc
+find "a" in "A%CC%8Abc" - grapheme_stristr = false == false
+find "Ab" in "a%CC%8Aabc" - grapheme_stristr = abc == abc
+find "abc" in "abcA%CC%8A" - grapheme_stristr = abcA%CC%8A == abcA%CC%8A
+find "A%CC%8Abc" in "aa%CC%8Abc" - grapheme_stristr = a%CC%8Abc == a%CC%8Abc
+find "a%CC%8A" in "aA%CC%8Abc" - grapheme_stristr before flag is FALSE = A%CC%8Abc == A%CC%8Abc
+find "A" in "aa%CC%8Abc" - grapheme_stristr before flag is TRUE = ==
+find "b" in "a%CC%8AaBc" - grapheme_stristr before flag is TRUE = a%CC%8Aa == a%CC%8Aa
+find "C" in "aba%CC%8Ac" - grapheme_stristr before flag is TRUE = aba%CC%8A == aba%CC%8A
+find "baa%CC%8Ab" in "abaA%CC%8Abc" - grapheme_stristr before flag is TRUE = a == a
+find "aBcA%CC%8A" in "ababca%CC%8A" - grapheme_stristr before flag is TRUE = ab == ab
+find "aba%CC%8Ac" in "abABA%CC%8Ac" - grapheme_stristr before flag is FALSE = ABA%CC%8Ac == ABA%CC%8Ac
+
+function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_COUNT, $start = 0[, $next])
+
+extract from "abc" "3" graphemes - grapheme_extract = abc == abc
+extract from "abc" "2" graphemes - grapheme_extract = ab == ab
+extract from "abc" "1" graphemes - grapheme_extract = a == a
+extract from "abc" "0" graphemes - grapheme_extract = ==
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 0 = a == a
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 1 = b == b
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 2 = c == c
+extract from "abc" "0" graphemes - grapheme_extract starting at byte position 2 = ==
+extract from "abc" "3" graphemes - grapheme_extract starting at byte position 0 with $next = abc == abc $next=3 == 3
+extract from "abc" "2" graphemes - grapheme_extract starting at byte position 0 with $next = ab == ab $next=2 == 2
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a == a $next=1 == 1
+extract from "abc" "0" graphemes - grapheme_extract starting at byte position 0 with $next = == $next=0 == 0
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a == a $next=1 == 1
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 1 with $next = b == b $next=2 == 2
+extract from "abc" "1" graphemes - grapheme_extract starting at byte position 2 with $next = c == c $next=3 == 3
+extract from "abc" "0" graphemes - grapheme_extract starting at byte position 2 with $next = == $next=2 == 2
+extract from "a%CC%8Abc" "3" graphemes - grapheme_extract = a%CC%8Abc == a%CC%8Abc
+extract from "a%CC%8Abc" "2" graphemes - grapheme_extract = a%CC%8Ab == a%CC%8Ab
+extract from "a%CC%8Abc" "1" graphemes - grapheme_extract = a%CC%8A == a%CC%8A
+extract from "a%CC%8Abc" "3" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8Abc == a%CC%8Abc $next=5 == 5
+extract from "a%CC%8Abc" "2" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8Ab == a%CC%8Ab $next=4 == 4
+extract from "a%CC%8Abc" "1" graphemes - grapheme_extract starting at byte position 0 with $next = a%CC%8A == a%CC%8A $next=3 == 3
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 3 with $next = bc == bc $next=5 == 5
+extract from "a%CC%8Abcde" "2" graphemes - grapheme_extract starting at byte position 4 with $next = cd == cd $next=6 == 6
+extract from "a%CC%8Abcdea%CC%8Af" "4" graphemes - grapheme_extract starting at byte position 5 with $next = dea%CC%8Af == dea%CC%8Af $next=11 == 11
+extract from "a%CC%8Ao%CC%88o%CC%88" "3" graphemes - grapheme_extract = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
+extract from "a%CC%8Ao%CC%88o%CC%88" "2" graphemes - grapheme_extract = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88
+extract from "a%CC%8Ao%CC%88c" "1" graphemes - grapheme_extract = a%CC%8A == a%CC%8A
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "1" graphemes - grapheme_extract starting at byte position 0 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "1" graphemes - grapheme_extract starting at byte position 2 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "1" graphemes - grapheme_extract starting at byte position 3 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "1" graphemes - grapheme_extract starting at byte position 4 = %CC%88 == %CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 8 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 10 = %CC%88 == %CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 11 = false == false
+
+function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_MAXBYTES, $start = 0)
+
+extract from "abc" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = abc == abc
+extract from "abc" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = ab == ab
+extract from "abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a == a
+extract from "abc" "0" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = ==
+extract from "a%CC%8Abc" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Abc == a%CC%8Abc
+extract from "a%CC%8Abc" "4" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ab == a%CC%8Ab
+extract from "a%CC%8Abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = ==
+extract from "a%CC%8Ao%CC%88o%CC%88" "9" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
+extract from "a%CC%8Ao%CC%88o%CC%88" "10" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
+extract from "a%CC%8Ao%CC%88o%CC%88" "11" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88
+extract from "a%CC%8Ao%CC%88o%CC%88" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88
+extract from "a%CC%8Ao%CC%88c" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8A == a%CC%8A
+extract from "a%CC%8Ao%CC%88c" "4" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8A == a%CC%8A
+extract from "a%CC%8Ao%CC%88c" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8A == a%CC%8A
+extract from "a%CC%8Ao%CC%88c" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88
+extract from "a%CC%8Ao%CC%88c" "7" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES = a%CC%8Ao%CC%88c == a%CC%8Ao%CC%88c
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 0 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 2 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 3 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 4 = %CC%88 == %CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 8 = o%CC%88 == o%CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 10 = %CC%88 == %CC%88
+extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXBYTES starting at byte position 11 = false == false
+
+function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_MAXCHARS, $start = 0)
+
+extract from "abc" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = abc == abc
+extract from "abc" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ab == ab
+extract from "abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = a == a
+extract from "abc" "0" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ==
+extract from "abco%CC%88" "0" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ==
+extract from "abco%CC%88" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = a == a
+extract from "abco%CC%88" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ab == ab
+extract from "abco%CC%88" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = abc == abc
+extract from "abco%CC%88" "4" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = abc == abc
+extract from "abco%CC%88" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = abco%CC%88 == abco%CC%88
+extract from "abco%CC%88" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = abco%CC%88 == abco%CC%88
+extract from "o%CC%88abc" "0" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ==
+extract from "o%CC%88abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = ==
+extract from "o%CC%88abc" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88 == o%CC%88
+extract from "o%CC%88abc" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88a == o%CC%88a
+extract from "o%CC%88abc" "4" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88ab == o%CC%88ab
+extract from "o%CC%88abca%CC%8Axyz" "5" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88abc == o%CC%88abc
+extract from "o%CC%88abca%CC%8Axyz" "6" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88abc == o%CC%88abc
+extract from "o%CC%88abca%CC%8Axyz" "7" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88abca%CC%8A == o%CC%88abca%CC%8A
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax
+extract from "abc" "3" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 0 = abc == abc
+extract from "abc" "2" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 1 = bc == bc
+extract from "abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 2 = c == c
+extract from "abc" "0" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 3 = false == false
+extract from "abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 3 = false == false
+extract from "abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 999 = false == false
+extract from "o%CC%88abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 6 = false == false
+extract from "o%CC%88abc" "1" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 999 = false == false
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 0 = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 1 = %CC%88abca%CC%8Axy == %CC%88abca%CC%8Axy
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 2 = abca%CC%8Axyz == abca%CC%8Axyz
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 3 = abca%CC%8Axyz == abca%CC%8Axyz
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 4 = bca%CC%8Axyz == bca%CC%8Axyz
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 5 = ca%CC%8Axyz == ca%CC%8Axyz
+extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extract GRAPHEME_EXTR_MAXCHARS starting at byte position 6 = a%CC%8Axyz == a%CC%8Axyz
+
diff --git a/ext/intl/tests/intl_error_name.phpt b/ext/intl/tests/intl_error_name.phpt
new file mode 100755
index 0000000000..8f5b2dc198
--- /dev/null
+++ b/ext/intl/tests/intl_error_name.phpt
@@ -0,0 +1,25 @@
+--TEST--
+intl_error_name()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Check getting error string by integer error code.
+ */
+
+
+function check( $err_code )
+{
+ echo intl_error_name( $err_code ) . "\n";
+}
+
+check( U_ZERO_ERROR );
+check( U_ILLEGAL_ARGUMENT_ERROR );
+check( U_USING_FALLBACK_WARNING );
+?>
+--EXPECT--
+U_ZERO_ERROR
+U_ILLEGAL_ARGUMENT_ERROR
+U_USING_FALLBACK_WARNING
diff --git a/ext/intl/tests/intl_get_error_code.phpt b/ext/intl/tests/intl_get_error_code.phpt
new file mode 100755
index 0000000000..6cd361ba71
--- /dev/null
+++ b/ext/intl/tests/intl_get_error_code.phpt
@@ -0,0 +1,24 @@
+--TEST--
+intl_get_error_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check getting global error code.
+ */
+
+// Suppress warning messages.
+error_reporting( E_ERROR );
+
+if( collator_get_locale() !== false )
+ echo "failed\n";
+else
+{
+ $check_code = ( intl_get_error_code() != 0 );
+ echo ( $check_code ? "ok" : "failed" ) . "\n";
+}
+
+?>
+--EXPECT--
+ok
diff --git a/ext/intl/tests/intl_get_error_message.phpt b/ext/intl/tests/intl_get_error_message.phpt
new file mode 100755
index 0000000000..f81b5c03d5
--- /dev/null
+++ b/ext/intl/tests/intl_get_error_message.phpt
@@ -0,0 +1,21 @@
+--TEST--
+intl_get_error_message()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check getting global error message.
+ */
+
+// Suppress warning messages.
+error_reporting( E_ERROR );
+
+if( collator_get_locale() !== false )
+ echo "failed\n";
+else
+ printf( "%s\n", intl_get_error_message() );
+
+?>
+--EXPECT--
+collator_get_locale: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/intl_is_failure.phpt b/ext/intl/tests/intl_is_failure.phpt
new file mode 100755
index 0000000000..e07df8f2c9
--- /dev/null
+++ b/ext/intl/tests/intl_is_failure.phpt
@@ -0,0 +1,25 @@
+--TEST--
+intl_is_failure()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check determining failure error codes.
+ */
+
+
+function check( $err_code )
+{
+ var_export( intl_is_failure( $err_code ) );
+ echo "\n";
+}
+
+check( U_ZERO_ERROR );
+check( U_USING_FALLBACK_WARNING );
+check( U_ILLEGAL_ARGUMENT_ERROR );
+?>
+--EXPECT--
+false
+false
+true
diff --git a/ext/intl/tests/locale_compose_locale.phpt b/ext/intl/tests/locale_compose_locale.phpt
new file mode 100755
index 0000000000..3fb07be41b
--- /dev/null
+++ b/ext/intl/tests/locale_compose_locale.phpt
@@ -0,0 +1,197 @@
+--TEST--
+locale_compose_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $longstr = str_repeat("blah", 500);
+ $loc_parts_arr1 = array(
+ Locale::LANG_TAG =>'sl' ,
+ Locale::SCRIPT_TAG =>'Latn' ,
+ Locale::REGION_TAG =>'IT' ,
+ Locale::VARIANT_TAG => $longstr
+ );
+ $loc_parts_arr2 = array(
+ Locale::LANG_TAG =>'de' ,
+ Locale::REGION_TAG =>'DE'
+ );
+ $loc_parts_arr3 = array(
+ Locale::LANG_TAG =>'hi'
+ );
+ $loc_parts_arr4 = array(
+ Locale::LANG_TAG =>'zh' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN'
+ );
+ $loc_parts_arr5 = array(
+ Locale::LANG_TAG =>'es' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN'
+ );
+ $loc_parts_arr6 = array(
+ Locale::LANG_TAG =>'en' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN',
+ Locale::VARIANT_TAG.'14' =>'rozaj' ,
+ 'variant1'=>'nedis'
+ );
+ $loc_parts_arr7 = array(
+ Locale::LANG_TAG =>'en' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN',
+ 'variant14'=>'rozaj' ,
+ 'variant1'=>'nedis' ,
+ 'extlang0'=>'lng' ,
+ 'extlang1'=>'ing'
+ );
+ $loc_parts_arr8 = array(
+ Locale::LANG_TAG =>'en' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN',
+ 'variant14'=>'rozaj' ,
+ 'variant1'=>'nedis' ,
+ 'extlang0'=>'lng' ,
+ 'extlang1'=>'ing',
+ 'private7'=>'prv1' ,
+ 'private9'=>'prv2'
+ );
+ $loc_parts_arr9 = array(
+ Locale::REGION_TAG =>'DE'
+ );
+ $loc_parts_arr10 = array(
+ Locale::LANG_TAG => $longstr
+ );
+ $loc_parts_arr11 = array(
+ Locale::LANG_TAG =>'en' ,
+ 'private0' => $longstr
+ );
+ $loc_parts_arr12 = array(
+ Locale::LANG_TAG => 45,
+ Locale::REGION_TAG => false,
+ Locale::SCRIPT_TAG => 15
+ );
+ $loc_parts_arr13 = array(
+ Locale::LANG_TAG =>'de' ,
+ Locale::REGION_TAG =>'DE',
+ 'private0' => 13,
+ 'variant1' => array(),
+ 'extlang2' => false
+ );
+
+ $loc_parts_arr = array(
+ 'loc1' => $loc_parts_arr1 ,
+ 'loc2' => $loc_parts_arr2 ,
+ 'loc3' => $loc_parts_arr3 ,
+ 'loc4' => $loc_parts_arr4 ,
+ 'loc5' => $loc_parts_arr5 ,
+ 'loc6' => $loc_parts_arr6 ,
+ 'loc7' => $loc_parts_arr7 ,
+ 'loc8' => $loc_parts_arr8 ,
+ 'loc9' => $loc_parts_arr9 ,
+ 'loc10' => $loc_parts_arr10 ,
+ 'loc11' => $loc_parts_arr11 ,
+ 'loc12' => $loc_parts_arr12 ,
+ 'loc13' => $loc_parts_arr13
+ );
+
+ error_reporting( E_ERROR );
+
+ $cnt = 0;
+ $res_str = '';
+ foreach($loc_parts_arr as $key => $value ){
+ $res_str .= "\n------------";
+ $res_str .= "\nInput Array name is : loc".(++$cnt) ;
+/*
+ foreach($value as $valKey => $valValue ){
+ $res_str .= $valKey ."->".$valValue." " ;
+ }
+*/
+
+ $locale = ut_loc_locale_compose( $value);
+ $res_str .= "\n\nComposed Locale: ";
+ if( $locale){
+ $res_str .= "$locale";
+ }else{
+ $res_str .= "No values found from Locale compose due to the following error:\n";
+ $res_str .= intl_get_error_message() ;
+ }
+ }
+
+ $res_str .= "\n------------";
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+------------
+Input Array name is : loc1
+
+Composed Locale: No values found from Locale compose due to the following error:
+Aborting locale_compose: array element will cause the buffer overflow. Maximum size allowed for locale_compose parameters is 512 bytes including separator character and prefixes. : U_BUFFER_OVERFLOW_ERROR
+------------
+Input Array name is : loc2
+
+Composed Locale: de_DE
+------------
+Input Array name is : loc3
+
+Composed Locale: hi
+------------
+Input Array name is : loc4
+
+Composed Locale: zh_Hans_CN
+------------
+Input Array name is : loc5
+
+Composed Locale: es_Hans_CN
+------------
+Input Array name is : loc6
+
+Composed Locale: en_Hans_CN_nedis_rozaj
+------------
+Input Array name is : loc7
+
+Composed Locale: en_lng_ing_Hans_CN_nedis_rozaj
+------------
+Input Array name is : loc8
+
+Composed Locale: en_lng_ing_Hans_CN_nedis_rozaj_x_prv1_prv2
+------------
+Input Array name is : loc9
+
+Composed Locale: No values found from Locale compose due to the following error:
+locale_compose: parameter array does not contain 'language' tag.: U_ILLEGAL_ARGUMENT_ERROR
+------------
+Input Array name is : loc10
+
+Composed Locale: No values found from Locale compose due to the following error:
+Aborting locale_compose: array element will cause the buffer overflow. Maximum size allowed for locale_compose parameters is 512 bytes including separator character and prefixes. : U_BUFFER_OVERFLOW_ERROR
+------------
+Input Array name is : loc11
+
+Composed Locale: No values found from Locale compose due to the following error:
+Aborting locale_compose: array element will cause the buffer overflow. Maximum size allowed for locale_compose parameters is 512 bytes including separator character and prefixes. : U_BUFFER_OVERFLOW_ERROR
+------------
+Input Array name is : loc12
+
+Composed Locale: No values found from Locale compose due to the following error:
+Aborting locale_compose: parameter array element is not a string : U_ILLEGAL_ARGUMENT_ERROR
+------------
+Input Array name is : loc13
+
+Composed Locale: No values found from Locale compose due to the following error:
+Aborting locale_compose: parameter array element is not a string : U_ILLEGAL_ARGUMENT_ERROR
+------------ \ No newline at end of file
diff --git a/ext/intl/tests/locale_filter_matches.phpt b/ext/intl/tests/locale_filter_matches.phpt
new file mode 100755
index 0000000000..19e760e8a4
--- /dev/null
+++ b/ext/intl/tests/locale_filter_matches.phpt
@@ -0,0 +1,365 @@
+--TEST--
+locale_filter_matches.phpt()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $loc_ranges = array(
+ 'de-de',
+ 'sl_IT',
+ 'sl_IT_Nedis',
+ 'jbo',
+ 'art-lojban',
+ 'sl_IT'
+ );
+
+ $lang_tags = array(
+ 'de-DEVA',
+ 'de-DE-1996',
+ 'de-DE',
+ 'zh_Hans',
+ 'de-CH-1996',
+ 'sl_IT',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'sgn-CH-de',
+ 'art-lojban',
+ 'i-lux',
+ 'art-lojban',
+ 'jbo',
+ 'en_sl_IT'
+ );
+
+ $res_str = '';
+ $isCanonical = false;
+ foreach($loc_ranges as $loc_range){
+ foreach($lang_tags as $lang_tag){
+ $res_str .="--------------\n";
+ $result= ut_loc_locale_filter_matches( $lang_tag , $loc_range , $isCanonical);
+ $res_str .= "loc_range:$loc_range matches lang_tag $lang_tag ? ";
+ if( $result){
+ $res_str .= "YES\n";
+ }else{
+ $res_str .= "NO\n";
+ }
+//canonicalized version
+ $result= ut_loc_locale_filter_matches( $lang_tag , $loc_range , !($isCanonical));
+ $can_loc_range = ut_loc_canonicalize($loc_range);
+ $can_lang_tag = ut_loc_canonicalize($lang_tag);
+ $res_str .= "loc_range:$can_loc_range canonically matches lang_tag $can_lang_tag ? ";
+ if( $result){
+ $res_str .= "YES\n";
+ }else{
+ $res_str .= "NO\n";
+ }
+ }
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+--------------
+loc_range:de-de matches lang_tag de-DEVA ? NO
+loc_range:de_DE canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:de-de matches lang_tag de-DE-1996 ? YES
+loc_range:de_DE canonically matches lang_tag de_DE_1996 ? YES
+--------------
+loc_range:de-de matches lang_tag de-DE ? YES
+loc_range:de_DE canonically matches lang_tag de_DE ? YES
+--------------
+loc_range:de-de matches lang_tag zh_Hans ? NO
+loc_range:de_DE canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:de-de matches lang_tag de-CH-1996 ? NO
+loc_range:de_DE canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:de-de matches lang_tag sl_IT ? NO
+loc_range:de_DE canonically matches lang_tag sl_IT ? NO
+--------------
+loc_range:de-de matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? NO
+loc_range:de_DE canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? NO
+--------------
+loc_range:de-de matches lang_tag sl_IT_rozaj ? NO
+loc_range:de_DE canonically matches lang_tag sl_IT_ROZAJ ? NO
+--------------
+loc_range:de-de matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+loc_range:de_DE canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+--------------
+loc_range:de-de matches lang_tag i-enochian ? NO
+loc_range:de_DE canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:de-de matches lang_tag sgn-CH-de ? NO
+loc_range:de_DE canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:de-de matches lang_tag art-lojban ? NO
+loc_range:de_DE canonically matches lang_tag jbo ? NO
+--------------
+loc_range:de-de matches lang_tag i-lux ? NO
+loc_range:de_DE canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:de-de matches lang_tag art-lojban ? NO
+loc_range:de_DE canonically matches lang_tag jbo ? NO
+--------------
+loc_range:de-de matches lang_tag jbo ? NO
+loc_range:de_DE canonically matches lang_tag jbo ? NO
+--------------
+loc_range:de-de matches lang_tag en_sl_IT ? NO
+loc_range:de_DE canonically matches lang_tag en_SL_IT ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DEVA ? NO
+loc_range:sl_IT canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DE-1996 ? NO
+loc_range:sl_IT canonically matches lang_tag de_DE_1996 ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DE ? NO
+loc_range:sl_IT canonically matches lang_tag de_DE ? NO
+--------------
+loc_range:sl_IT matches lang_tag zh_Hans ? NO
+loc_range:sl_IT canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-CH-1996 ? NO
+loc_range:sl_IT canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:sl_IT matches lang_tag sl_IT ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_rozaj ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_ROZAJ ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+--------------
+loc_range:sl_IT matches lang_tag i-enochian ? NO
+loc_range:sl_IT canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:sl_IT matches lang_tag sgn-CH-de ? NO
+loc_range:sl_IT canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:sl_IT matches lang_tag art-lojban ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag i-lux ? NO
+loc_range:sl_IT canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:sl_IT matches lang_tag art-lojban ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag jbo ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag en_sl_IT ? NO
+loc_range:sl_IT canonically matches lang_tag en_SL_IT ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag de-DEVA ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag de-DE-1996 ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag de_DE_1996 ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag de-DE ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag de_DE ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag zh_Hans ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag de-CH-1996 ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag sl_IT ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag sl_IT ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? YES
+loc_range:sl_IT_NEDIS canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? YES
+--------------
+loc_range:sl_IT_Nedis matches lang_tag sl_IT_rozaj ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag sl_IT_ROZAJ ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+loc_range:sl_IT_NEDIS canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+--------------
+loc_range:sl_IT_Nedis matches lang_tag i-enochian ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag sgn-CH-de ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag art-lojban ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag i-lux ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag art-lojban ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag jbo ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT_Nedis matches lang_tag en_sl_IT ? NO
+loc_range:sl_IT_NEDIS canonically matches lang_tag en_SL_IT ? NO
+--------------
+loc_range:jbo matches lang_tag de-DEVA ? NO
+loc_range:jbo canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:jbo matches lang_tag de-DE-1996 ? NO
+loc_range:jbo canonically matches lang_tag de_DE_1996 ? NO
+--------------
+loc_range:jbo matches lang_tag de-DE ? NO
+loc_range:jbo canonically matches lang_tag de_DE ? NO
+--------------
+loc_range:jbo matches lang_tag zh_Hans ? NO
+loc_range:jbo canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:jbo matches lang_tag de-CH-1996 ? NO
+loc_range:jbo canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:jbo matches lang_tag sl_IT ? NO
+loc_range:jbo canonically matches lang_tag sl_IT ? NO
+--------------
+loc_range:jbo matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? NO
+--------------
+loc_range:jbo matches lang_tag sl_IT_rozaj ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_ROZAJ ? NO
+--------------
+loc_range:jbo matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+--------------
+loc_range:jbo matches lang_tag i-enochian ? NO
+loc_range:jbo canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:jbo matches lang_tag sgn-CH-de ? NO
+loc_range:jbo canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:jbo matches lang_tag art-lojban ? NO
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:jbo matches lang_tag i-lux ? NO
+loc_range:jbo canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:jbo matches lang_tag art-lojban ? NO
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:jbo matches lang_tag jbo ? YES
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:jbo matches lang_tag en_sl_IT ? NO
+loc_range:jbo canonically matches lang_tag en_SL_IT ? NO
+--------------
+loc_range:art-lojban matches lang_tag de-DEVA ? NO
+loc_range:jbo canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:art-lojban matches lang_tag de-DE-1996 ? NO
+loc_range:jbo canonically matches lang_tag de_DE_1996 ? NO
+--------------
+loc_range:art-lojban matches lang_tag de-DE ? NO
+loc_range:jbo canonically matches lang_tag de_DE ? NO
+--------------
+loc_range:art-lojban matches lang_tag zh_Hans ? NO
+loc_range:jbo canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:art-lojban matches lang_tag de-CH-1996 ? NO
+loc_range:jbo canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:art-lojban matches lang_tag sl_IT ? NO
+loc_range:jbo canonically matches lang_tag sl_IT ? NO
+--------------
+loc_range:art-lojban matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? NO
+--------------
+loc_range:art-lojban matches lang_tag sl_IT_rozaj ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_ROZAJ ? NO
+--------------
+loc_range:art-lojban matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+loc_range:jbo canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? NO
+--------------
+loc_range:art-lojban matches lang_tag i-enochian ? NO
+loc_range:jbo canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:art-lojban matches lang_tag sgn-CH-de ? NO
+loc_range:jbo canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:art-lojban matches lang_tag art-lojban ? YES
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:art-lojban matches lang_tag i-lux ? NO
+loc_range:jbo canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:art-lojban matches lang_tag art-lojban ? YES
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:art-lojban matches lang_tag jbo ? NO
+loc_range:jbo canonically matches lang_tag jbo ? YES
+--------------
+loc_range:art-lojban matches lang_tag en_sl_IT ? NO
+loc_range:jbo canonically matches lang_tag en_SL_IT ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DEVA ? NO
+loc_range:sl_IT canonically matches lang_tag de_Deva ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DE-1996 ? NO
+loc_range:sl_IT canonically matches lang_tag de_DE_1996 ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-DE ? NO
+loc_range:sl_IT canonically matches lang_tag de_DE ? NO
+--------------
+loc_range:sl_IT matches lang_tag zh_Hans ? NO
+loc_range:sl_IT canonically matches lang_tag zh_Hans ? NO
+--------------
+loc_range:sl_IT matches lang_tag de-CH-1996 ? NO
+loc_range:sl_IT canonically matches lang_tag de_CH_1996 ? NO
+--------------
+loc_range:sl_IT matches lang_tag sl_IT ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_nedis-a-kirti-x-xyz ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_NEDIS_A_KIRTI_X_XYZ ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_rozaj ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_ROZAJ ? YES
+--------------
+loc_range:sl_IT matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+loc_range:sl_IT canonically matches lang_tag sl_IT_NEDIS_ROJAZ_1901 ? YES
+--------------
+loc_range:sl_IT matches lang_tag i-enochian ? NO
+loc_range:sl_IT canonically matches lang_tag i-enochian ? NO
+--------------
+loc_range:sl_IT matches lang_tag sgn-CH-de ? NO
+loc_range:sl_IT canonically matches lang_tag sgn_CH_DE ? NO
+--------------
+loc_range:sl_IT matches lang_tag art-lojban ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag i-lux ? NO
+loc_range:sl_IT canonically matches lang_tag i-lux ? NO
+--------------
+loc_range:sl_IT matches lang_tag art-lojban ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag jbo ? NO
+loc_range:sl_IT canonically matches lang_tag jbo ? NO
+--------------
+loc_range:sl_IT matches lang_tag en_sl_IT ? NO
+loc_range:sl_IT canonically matches lang_tag en_SL_IT ? NO
diff --git a/ext/intl/tests/locale_get_all_variants.phpt b/ext/intl/tests/locale_get_all_variants.phpt
new file mode 100755
index 0000000000..864b8a2e1a
--- /dev/null
+++ b/ext/intl/tests/locale_get_all_variants.phpt
@@ -0,0 +1,62 @@
+--TEST--
+locale_get_all_variants.phpt()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'sl_IT_nedis_KIRTI',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'zh-hakka',
+ 'zh-wuu',
+ 'i-tay',
+ 'sgn-BE-nl',
+ 'sgn-CH-de',
+ 'sl_IT_rozaj@currency=EUR'
+ );
+ $res_str = '';
+ foreach($locales as $locale){
+ $variants_arr = ut_loc_locale_get_all_variants( $locale);
+ $res_str .= "$locale : variants ";
+ if( $variants_arr){
+ foreach($variants_arr as $variant){
+ $res_str .= "'$variant',";
+ }
+ }else{
+ $res_str .= "--none";
+ }
+ $res_str .= "\n";
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+sl_IT_nedis_KIRTI : variants 'NEDIS','KIRTI',
+sl_IT_nedis-a-kirti-x-xyz : variants 'NEDIS',
+sl_IT_rozaj : variants 'ROZAJ',
+sl_IT_NEDIS_ROJAZ_1901 : variants 'NEDIS','ROJAZ','1901',
+i-enochian : variants --none
+zh-hakka : variants --none
+zh-wuu : variants --none
+i-tay : variants --none
+sgn-BE-nl : variants --none
+sgn-CH-de : variants --none
+sl_IT_rozaj@currency=EUR : variants 'ROZAJ',
diff --git a/ext/intl/tests/locale_get_default.phpt b/ext/intl/tests/locale_get_default.phpt
new file mode 100755
index 0000000000..57c46ccd2b
--- /dev/null
+++ b/ext/intl/tests/locale_get_default.phpt
@@ -0,0 +1,46 @@
+--TEST--
+locale_get_default()
+--INI--
+intl.default_locale=en-US
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the default Locale with different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ locale_set_default('de-DE');
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ ini_set('intl.default_locale', 'fr');
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ ini_restore("intl.default_locale");
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Default locale: en-US
+Default locale: de-DE
+Default locale: fr
diff --git a/ext/intl/tests/locale_get_display_language.phpt b/ext/intl/tests/locale_get_display_language.phpt
new file mode 100755
index 0000000000..3b61cae3ac
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_language.phpt
@@ -0,0 +1,280 @@
+--TEST--
+locale_get_display_language()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_language 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)
+ 'art-lojban', //(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_language( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_language=$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_language=Ukrainian
+disp_locale=fr : display_language=ukrainien
+disp_locale=de : display_language=Ukrainisch
+-----------------
+locale='root'
+disp_locale=en : display_language=Root
+disp_locale=fr : display_language=racine
+disp_locale=de : display_language=root
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_language=Ukrainian
+disp_locale=fr : display_language=ukrainien
+disp_locale=de : display_language=Ukrainisch
+-----------------
+locale='Hindi'
+disp_locale=en : display_language=hindi
+disp_locale=fr : display_language=hindi
+disp_locale=de : display_language=hindi
+-----------------
+locale='de'
+disp_locale=en : display_language=German
+disp_locale=fr : display_language=allemand
+disp_locale=de : display_language=Deutsch
+-----------------
+locale='fr'
+disp_locale=en : display_language=French
+disp_locale=fr : display_language=français
+disp_locale=de : display_language=Französisch
+-----------------
+locale='ja'
+disp_locale=en : display_language=Japanese
+disp_locale=fr : display_language=japonais
+disp_locale=de : display_language=Japanisch
+-----------------
+locale='i-enochian'
+disp_locale=en : display_language=i-enochian
+disp_locale=fr : display_language=i-enochian
+disp_locale=de : display_language=i-enochian
+-----------------
+locale='art-lojban'
+disp_locale=en : display_language=Lojban
+disp_locale=fr : display_language=lojban
+disp_locale=de : display_language=Lojban
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='sr-Cyrl'
+disp_locale=en : display_language=Serbian
+disp_locale=fr : display_language=serbe
+disp_locale=de : display_language=Serbisch
+-----------------
+locale='sr-Latn'
+disp_locale=en : display_language=Serbian
+disp_locale=fr : display_language=serbe
+disp_locale=de : display_language=Serbisch
+-----------------
+locale='zh-Hans-CN'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_language=Serbian
+disp_locale=fr : display_language=serbe
+disp_locale=de : display_language=Serbisch
+-----------------
+locale='sl-rozaj'
+disp_locale=en : display_language=Slovenian
+disp_locale=fr : display_language=slovène
+disp_locale=de : display_language=Slowenisch
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_language=Slovenian
+disp_locale=fr : display_language=slovène
+disp_locale=de : display_language=Slowenisch
+-----------------
+locale='de-CH-1901'
+disp_locale=en : display_language=German
+disp_locale=fr : display_language=allemand
+disp_locale=de : display_language=Deutsch
+-----------------
+locale='sl-IT-nedis'
+disp_locale=en : display_language=Slovenian
+disp_locale=fr : display_language=slovène
+disp_locale=de : display_language=Slowenisch
+-----------------
+locale='sl-Latn-IT-nedis'
+disp_locale=en : display_language=Slovenian
+disp_locale=fr : display_language=slovène
+disp_locale=de : display_language=Slowenisch
+-----------------
+locale='de-DE'
+disp_locale=en : display_language=German
+disp_locale=fr : display_language=allemand
+disp_locale=de : display_language=Deutsch
+-----------------
+locale='en-US'
+disp_locale=en : display_language=English
+disp_locale=fr : display_language=anglais
+disp_locale=de : display_language=Englisch
+-----------------
+locale='es-419'
+disp_locale=en : display_language=Spanish
+disp_locale=fr : display_language=espagnol
+disp_locale=de : display_language=Spanisch
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_language=German
+disp_locale=fr : display_language=allemand
+disp_locale=de : display_language=Deutsch
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_language=Azerbaijani
+disp_locale=fr : display_language=azéri
+disp_locale=de : display_language=Aserbaidschanisch
+-----------------
+locale='zh-min'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='zh-min-nan-Hant-CN'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='x-whatever'
+disp_locale=en : display_language=x-whatever
+disp_locale=fr : display_language=x-whatever
+disp_locale=de : display_language=x-whatever
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_language=qaa
+disp_locale=fr : display_language=qaa
+disp_locale=de : display_language=qaa
+-----------------
+locale='sr-Latn-QM'
+disp_locale=en : display_language=Serbian
+disp_locale=fr : display_language=serbe
+disp_locale=de : display_language=Serbisch
+-----------------
+locale='sr-Qaaa-CS'
+disp_locale=en : display_language=Serbian
+disp_locale=fr : display_language=serbe
+disp_locale=de : display_language=Serbisch
+-----------------
+locale='en-US-u-islamCal'
+disp_locale=en : display_language=English
+disp_locale=fr : display_language=anglais
+disp_locale=de : display_language=Englisch
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_language=Chinese
+disp_locale=fr : display_language=chinois
+disp_locale=de : display_language=Chinesisch
+-----------------
+locale='en-a-myExt-b-another'
+disp_locale=en : display_language=English
+disp_locale=fr : display_language=anglais
+disp_locale=de : display_language=Englisch
+-----------------
+locale='de-419-DE'
+disp_locale=en : display_language=German
+disp_locale=fr : display_language=allemand
+disp_locale=de : display_language=Deutsch
+-----------------
+locale='a-DE'
+disp_locale=en : display_language=a
+disp_locale=fr : display_language=a
+disp_locale=de : display_language=a
+-----------------
+locale='ar-a-aaa-b-bbb-a-ccc'
+disp_locale=en : display_language=Arabic
+disp_locale=fr : display_language=arabe
+disp_locale=de : display_language=Arabisch
+-----------------
diff --git a/ext/intl/tests/locale_get_display_name.phpt b/ext/intl/tests/locale_get_display_name.phpt
new file mode 100755
index 0000000000..5082b63c5f
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_name.phpt
@@ -0,0 +1,340 @@
+--TEST--
+locale_get_display_name()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_name for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str='';
+
+ $disp_locales=array('en','fr','de');
+
+ $locales = array(
+ 'sl_IT_nedis_KIRTI',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'zh-hakka',
+ 'zh-wuu',
+ 'i-tay',
+ 'sgn-BE-nl',
+ 'sgn-CH-de',
+ 'sl_IT_rozaj@currency=EUR',
+ '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_name( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_name=$scr";
+ $res_str .= "\n";
+ }
+ $res_str .= "-----------------\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+locale='sl_IT_nedis_KIRTI'
+disp_locale=en : display_name=Slovenian (Italy, NEDIS_KIRTI)
+disp_locale=fr : display_name=slovène (Italie, NEDIS_KIRTI)
+disp_locale=de : display_name=Slowenisch (Italien, NEDIS_KIRTI)
+-----------------
+locale='sl_IT_nedis-a-kirti-x-xyz'
+disp_locale=en : display_name=Slovenian (Italy, NEDIS_A_KIRTI_X_XYZ)
+disp_locale=fr : display_name=slovène (Italie, NEDIS_A_KIRTI_X_XYZ)
+disp_locale=de : display_name=Slowenisch (Italien, NEDIS_A_KIRTI_X_XYZ)
+-----------------
+locale='sl_IT_rozaj'
+disp_locale=en : display_name=Slovenian (Italy, Resian)
+disp_locale=fr : display_name=slovène (Italie, dialecte de Resia)
+disp_locale=de : display_name=Slowenisch (Italien, ROZAJ)
+-----------------
+locale='sl_IT_NEDIS_ROJAZ_1901'
+disp_locale=en : display_name=Slovenian (Italy, NEDIS_ROJAZ_1901)
+disp_locale=fr : display_name=slovène (Italie, NEDIS_ROJAZ_1901)
+disp_locale=de : display_name=Slowenisch (Italien, NEDIS_ROJAZ_1901)
+-----------------
+locale='i-enochian'
+disp_locale=en : display_name=i-enochian
+disp_locale=fr : display_name=i-enochian
+disp_locale=de : display_name=i-enochian
+-----------------
+locale='zh-hakka'
+disp_locale=en : display_name=Chinese (HAKKA)
+disp_locale=fr : display_name=chinois (HAKKA)
+disp_locale=de : display_name=Chinesisch (HAKKA)
+-----------------
+locale='zh-wuu'
+disp_locale=en : display_name=Chinese (WUU)
+disp_locale=fr : display_name=chinois (WUU)
+disp_locale=de : display_name=Chinesisch (WUU)
+-----------------
+locale='i-tay'
+disp_locale=en : display_name=i-tay
+disp_locale=fr : display_name=i-tay
+disp_locale=de : display_name=i-tay
+-----------------
+locale='sgn-BE-nl'
+disp_locale=en : display_name=Sign Languages (Belgium, NL)
+disp_locale=fr : display_name=langues des signes (Belgique, NL)
+disp_locale=de : display_name=Gebärdensprache (Belgien, NL)
+-----------------
+locale='sgn-CH-de'
+disp_locale=en : display_name=Sign Languages (Switzerland, DE)
+disp_locale=fr : display_name=langues des signes (Suisse, DE)
+disp_locale=de : display_name=Gebärdensprache (Schweiz, DE)
+-----------------
+locale='sl_IT_rozaj@currency=EUR'
+disp_locale=en : display_name=Slovenian (Italy, Resian, Currency=Euro)
+disp_locale=fr : display_name=slovène (Italie, dialecte de Resia, Devise=euro)
+disp_locale=de : display_name=Slowenisch (Italien, ROZAJ, Währung=Euro)
+-----------------
+locale='uk-ua_CALIFORNIA@currency=;currency=GRN'
+disp_locale=en : display_name=Ukrainian (Ukraine, CALIFORNIA, Currency)
+disp_locale=fr : display_name=ukrainien (Ukraine, CALIFORNIA, Devise)
+disp_locale=de : display_name=Ukrainisch (Ukraine, CALIFORNIA, Währung)
+-----------------
+locale='root'
+disp_locale=en : display_name=Root
+disp_locale=fr : display_name=racine
+disp_locale=de : display_name=root
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_name=Ukrainian (Currency=EURO)
+disp_locale=fr : display_name=ukrainien (Devise=EURO)
+disp_locale=de : display_name=Ukrainisch (Währung=EURO)
+-----------------
+locale='Hindi'
+disp_locale=en : display_name=hindi
+disp_locale=fr : display_name=hindi
+disp_locale=de : display_name=hindi
+-----------------
+locale='de'
+disp_locale=en : display_name=German
+disp_locale=fr : display_name=allemand
+disp_locale=de : display_name=Deutsch
+-----------------
+locale='fr'
+disp_locale=en : display_name=French
+disp_locale=fr : display_name=français
+disp_locale=de : display_name=Französisch
+-----------------
+locale='ja'
+disp_locale=en : display_name=Japanese
+disp_locale=fr : display_name=japonais
+disp_locale=de : display_name=Japanisch
+-----------------
+locale='i-enochian'
+disp_locale=en : display_name=i-enochian
+disp_locale=fr : display_name=i-enochian
+disp_locale=de : display_name=i-enochian
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_name=Chinese (Traditional Han)
+disp_locale=fr : display_name=chinois (idéogrammes han (variante traditionnelle))
+disp_locale=de : display_name=Chinesisch (Traditionelle Chinesische Schrift)
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_name=Chinese (Simplified Han)
+disp_locale=fr : display_name=chinois (idéogrammes han (variante simplifiée))
+disp_locale=de : display_name=Chinesisch (Vereinfachte Chinesische Schrift)
+-----------------
+locale='sr-Cyrl'
+disp_locale=en : display_name=Serbian (Cyrillic)
+disp_locale=fr : display_name=serbe (cyrillique)
+disp_locale=de : display_name=Serbisch (Kyrillisch)
+-----------------
+locale='sr-Latn'
+disp_locale=en : display_name=Serbian (Latin)
+disp_locale=fr : display_name=serbe (latin)
+disp_locale=de : display_name=Serbisch (Lateinisch)
+-----------------
+locale='zh-Hans-CN'
+disp_locale=en : display_name=Chinese (Simplified Han, China)
+disp_locale=fr : display_name=chinois (idéogrammes han (variante simplifiée), Chine)
+disp_locale=de : display_name=Chinesisch (Vereinfachte Chinesische Schrift, China)
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_name=Serbian (Latin, Serbia And Montenegro)
+disp_locale=fr : display_name=serbe (latin, Serbie-et-Monténégro)
+disp_locale=de : display_name=Serbisch (Lateinisch, Serbien und Montenegro)
+-----------------
+locale='sl-rozaj'
+disp_locale=en : display_name=Slovenian (ROZAJ)
+disp_locale=fr : display_name=slovène (ROZAJ)
+disp_locale=de : display_name=Slowenisch (ROZAJ)
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_name=Slovenian (NEDIS)
+disp_locale=fr : display_name=slovène (NEDIS)
+disp_locale=de : display_name=Slowenisch (NEDIS)
+-----------------
+locale='de-CH-1901'
+disp_locale=en : display_name=German (Switzerland, Traditional German orthography)
+disp_locale=fr : display_name=allemand (Suisse, orthographe allemande traditionnelle)
+disp_locale=de : display_name=Deutsch (Schweiz, 1901)
+-----------------
+locale='sl-IT-nedis'
+disp_locale=en : display_name=Slovenian (Italy, Natisone dialect)
+disp_locale=fr : display_name=slovène (Italie, dialecte de Natisone)
+disp_locale=de : display_name=Slowenisch (Italien, NEDIS)
+-----------------
+locale='sl-Latn-IT-nedis'
+disp_locale=en : display_name=Slovenian (Latin, Italy, Natisone dialect)
+disp_locale=fr : display_name=slovène (latin, Italie, dialecte de Natisone)
+disp_locale=de : display_name=Slowenisch (Lateinisch, Italien, NEDIS)
+-----------------
+locale='de-DE'
+disp_locale=en : display_name=German (Germany)
+disp_locale=fr : display_name=allemand (Allemagne)
+disp_locale=de : display_name=Deutsch (Deutschland)
+-----------------
+locale='en-US'
+disp_locale=en : display_name=English (United States)
+disp_locale=fr : display_name=anglais (États-Unis)
+disp_locale=de : display_name=Englisch (Vereinigte Staaten)
+-----------------
+locale='es-419'
+disp_locale=en : display_name=Spanish (Latin America and the Caribbean)
+disp_locale=fr : display_name=espagnol (Amérique latine et Caraïbes)
+disp_locale=de : display_name=Spanisch (Lateinamerika und Karibik)
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_name=German (Switzerland, X_PHONEBK)
+disp_locale=fr : display_name=allemand (Suisse, X_PHONEBK)
+disp_locale=de : display_name=Deutsch (Schweiz, X_PHONEBK)
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_name=Azerbaijani (Arabic, X, AZE_DERBEND)
+disp_locale=fr : display_name=azéri (arabe, X, AZE_DERBEND)
+disp_locale=de : display_name=Aserbaidschanisch (Arabisch, X, AZE_DERBEND)
+-----------------
+locale='zh-min'
+disp_locale=en : display_name=Chinese (MIN)
+disp_locale=fr : display_name=chinois (MIN)
+disp_locale=de : display_name=Chinesisch (MIN)
+-----------------
+locale='zh-min-nan-Hant-CN'
+disp_locale=en : display_name=Chinese (MIN, NAN_HANT_CN)
+disp_locale=fr : display_name=chinois (MIN, NAN_HANT_CN)
+disp_locale=de : display_name=Chinesisch (MIN, NAN_HANT_CN)
+-----------------
+locale='x-whatever'
+disp_locale=en : display_name=x-whatever
+disp_locale=fr : display_name=x-whatever
+disp_locale=de : display_name=x-whatever
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_name=qaa (Qaaa, QM, X_SOUTHERN)
+disp_locale=fr : display_name=qaa (Qaaa, QM, X_SOUTHERN)
+disp_locale=de : display_name=qaa (Qaaa, QM, X_SOUTHERN)
+-----------------
+locale='sr-Latn-QM'
+disp_locale=en : display_name=Serbian (Latin, QM)
+disp_locale=fr : display_name=serbe (latin, QM)
+disp_locale=de : display_name=Serbisch (Lateinisch, QM)
+-----------------
+locale='sr-Qaaa-CS'
+disp_locale=en : display_name=Serbian (Qaaa, Serbia And Montenegro)
+disp_locale=fr : display_name=serbe (Qaaa, Serbie-et-Monténégro)
+disp_locale=de : display_name=Serbisch (Qaaa, Serbien und Montenegro)
+-----------------
+locale='en-US-u-islamCal'
+disp_locale=en : display_name=English (United States, U_ISLAMCAL)
+disp_locale=fr : display_name=anglais (États-Unis, U_ISLAMCAL)
+disp_locale=de : display_name=Englisch (Vereinigte Staaten, U_ISLAMCAL)
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_name=Chinese (China, A_MYEXT_X_PRIVATE)
+disp_locale=fr : display_name=chinois (Chine, A_MYEXT_X_PRIVATE)
+disp_locale=de : display_name=Chinesisch (China, A_MYEXT_X_PRIVATE)
+-----------------
+locale='en-a-myExt-b-another'
+disp_locale=en : display_name=English (A, MYEXT_B_ANOTHER)
+disp_locale=fr : display_name=anglais (A, MYEXT_B_ANOTHER)
+disp_locale=de : display_name=Englisch (A, MYEXT_B_ANOTHER)
+-----------------
+locale='de-419-DE'
+disp_locale=en : display_name=German (Latin America and the Caribbean, DE)
+disp_locale=fr : display_name=allemand (Amérique latine et Caraïbes, DE)
+disp_locale=de : display_name=Deutsch (Lateinamerika und Karibik, DE)
+-----------------
+locale='a-DE'
+disp_locale=en : display_name=a (Germany)
+disp_locale=fr : display_name=a (Allemagne)
+disp_locale=de : display_name=a (Deutschland)
+-----------------
+locale='ar-a-aaa-b-bbb-a-ccc'
+disp_locale=en : display_name=Arabic (A, AAA_B_BBB_A_CCC)
+disp_locale=fr : display_name=arabe (A, AAA_B_BBB_A_CCC)
+disp_locale=de : display_name=Arabisch (A, AAA_B_BBB_A_CCC)
+-----------------
diff --git a/ext/intl/tests/locale_get_display_region.phpt b/ext/intl/tests/locale_get_display_region.phpt
new file mode 100755
index 0000000000..ba90472e5d
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_region.phpt
@@ -0,0 +1,274 @@
+--TEST--
+locale_get_display_region()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_region 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_region( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_region=$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_region=Ukraine
+disp_locale=fr : display_region=Ukraine
+disp_locale=de : display_region=Ukraine
+-----------------
+locale='root'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='Hindi'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='de'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='fr'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='ja'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='i-enochian'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='sr-Cyrl'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='sr-Latn'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='zh-Hans-CN'
+disp_locale=en : display_region=China
+disp_locale=fr : display_region=Chine
+disp_locale=de : display_region=China
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_region=Serbia And Montenegro
+disp_locale=fr : display_region=Serbie-et-Monténégro
+disp_locale=de : display_region=Serbien und Montenegro
+-----------------
+locale='sl-rozaj'
+disp_locale=en : display_region=ROZAJ
+disp_locale=fr : display_region=ROZAJ
+disp_locale=de : display_region=ROZAJ
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_region=NEDIS
+disp_locale=fr : display_region=NEDIS
+disp_locale=de : display_region=NEDIS
+-----------------
+locale='de-CH-1901'
+disp_locale=en : display_region=Switzerland
+disp_locale=fr : display_region=Suisse
+disp_locale=de : display_region=Schweiz
+-----------------
+locale='sl-IT-nedis'
+disp_locale=en : display_region=Italy
+disp_locale=fr : display_region=Italie
+disp_locale=de : display_region=Italien
+-----------------
+locale='sl-Latn-IT-nedis'
+disp_locale=en : display_region=Italy
+disp_locale=fr : display_region=Italie
+disp_locale=de : display_region=Italien
+-----------------
+locale='de-DE'
+disp_locale=en : display_region=Germany
+disp_locale=fr : display_region=Allemagne
+disp_locale=de : display_region=Deutschland
+-----------------
+locale='en-US'
+disp_locale=en : display_region=United States
+disp_locale=fr : display_region=États-Unis
+disp_locale=de : display_region=Vereinigte Staaten
+-----------------
+locale='es-419'
+disp_locale=en : display_region=Latin America and the Caribbean
+disp_locale=fr : display_region=Amérique latine et Caraïbes
+disp_locale=de : display_region=Lateinamerika und Karibik
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_region=Switzerland
+disp_locale=fr : display_region=Suisse
+disp_locale=de : display_region=Schweiz
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_region=X
+disp_locale=fr : display_region=X
+disp_locale=de : display_region=X
+-----------------
+locale='zh-min'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='zh-min-nan-Hant-CN'
+disp_locale=en : display_region=MIN
+disp_locale=fr : display_region=MIN
+disp_locale=de : display_region=MIN
+-----------------
+locale='x-whatever'
+disp_locale=en : display_region=
+disp_locale=fr : display_region=
+disp_locale=de : display_region=
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_region=QM
+disp_locale=fr : display_region=QM
+disp_locale=de : display_region=QM
+-----------------
+locale='sr-Latn-QM'
+disp_locale=en : display_region=QM
+disp_locale=fr : display_region=QM
+disp_locale=de : display_region=QM
+-----------------
+locale='sr-Qaaa-CS'
+disp_locale=en : display_region=Serbia And Montenegro
+disp_locale=fr : display_region=Serbie-et-Monténégro
+disp_locale=de : display_region=Serbien und Montenegro
+-----------------
+locale='en-US-u-islamCal'
+disp_locale=en : display_region=United States
+disp_locale=fr : display_region=États-Unis
+disp_locale=de : display_region=Vereinigte Staaten
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_region=China
+disp_locale=fr : display_region=Chine
+disp_locale=de : display_region=China
+-----------------
+locale='en-a-myExt-b-another'
+disp_locale=en : display_region=A
+disp_locale=fr : display_region=A
+disp_locale=de : display_region=A
+-----------------
+locale='de-419-DE'
+disp_locale=en : display_region=Latin America and the Caribbean
+disp_locale=fr : display_region=Amérique latine et Caraïbes
+disp_locale=de : display_region=Lateinamerika und Karibik
+-----------------
+locale='a-DE'
+disp_locale=en : display_region=Germany
+disp_locale=fr : display_region=Allemagne
+disp_locale=de : display_region=Deutschland
+-----------------
+locale='ar-a-aaa-b-bbb-a-ccc'
+disp_locale=en : display_region=A
+disp_locale=fr : display_region=A
+disp_locale=de : display_region=A
+-----------------
diff --git a/ext/intl/tests/locale_get_display_script.phpt b/ext/intl/tests/locale_get_display_script.phpt
new file mode 100755
index 0000000000..1b74e90c96
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_script.phpt
@@ -0,0 +1,274 @@
+--TEST--
+locale_get_display_script()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) 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=idéogrammes han (variante traditionnelle)
+disp_locale=de : display_script=Traditionelle Chinesische Schrift
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_script=Simplified Han
+disp_locale=fr : display_script=idéogrammes han (variante simplifiée)
+disp_locale=de : display_script=Vereinfachte Chinesische Schrift
+-----------------
+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=idéogrammes han (variante simplifiée)
+disp_locale=de : display_script=Vereinfachte Chinesische Schrift
+-----------------
+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=
+----------------- \ No newline at end of file
diff --git a/ext/intl/tests/locale_get_display_variant.phpt b/ext/intl/tests/locale_get_display_variant.phpt
new file mode 100755
index 0000000000..34f3ba8ee0
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_variant.phpt
@@ -0,0 +1,274 @@
+--TEST--
+locale_get_display_variant()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_variant 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_variant( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_variant=$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_variant=CALIFORNIA
+disp_locale=fr : display_variant=CALIFORNIA
+disp_locale=de : display_variant=CALIFORNIA
+-----------------
+locale='root'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='Hindi'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='de'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='fr'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='ja'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='i-enochian'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sr-Cyrl'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sr-Latn'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='zh-Hans-CN'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sl-rozaj'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='de-CH-1901'
+disp_locale=en : display_variant=Traditional German orthography
+disp_locale=fr : display_variant=orthographe allemande traditionnelle
+disp_locale=de : display_variant=1901
+-----------------
+locale='sl-IT-nedis'
+disp_locale=en : display_variant=Natisone dialect
+disp_locale=fr : display_variant=dialecte de Natisone
+disp_locale=de : display_variant=NEDIS
+-----------------
+locale='sl-Latn-IT-nedis'
+disp_locale=en : display_variant=Natisone dialect
+disp_locale=fr : display_variant=dialecte de Natisone
+disp_locale=de : display_variant=NEDIS
+-----------------
+locale='de-DE'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='en-US'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='es-419'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_variant=X_PHONEBK
+disp_locale=fr : display_variant=X_PHONEBK
+disp_locale=de : display_variant=X_PHONEBK
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_variant=AZE_DERBEND
+disp_locale=fr : display_variant=AZE_DERBEND
+disp_locale=de : display_variant=AZE_DERBEND
+-----------------
+locale='zh-min'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='zh-min-nan-Hant-CN'
+disp_locale=en : display_variant=NAN_HANT_CN
+disp_locale=fr : display_variant=NAN_HANT_CN
+disp_locale=de : display_variant=NAN_HANT_CN
+-----------------
+locale='x-whatever'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_variant=X_SOUTHERN
+disp_locale=fr : display_variant=X_SOUTHERN
+disp_locale=de : display_variant=X_SOUTHERN
+-----------------
+locale='sr-Latn-QM'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='sr-Qaaa-CS'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='en-US-u-islamCal'
+disp_locale=en : display_variant=U_ISLAMCAL
+disp_locale=fr : display_variant=U_ISLAMCAL
+disp_locale=de : display_variant=U_ISLAMCAL
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_variant=A_MYEXT_X_PRIVATE
+disp_locale=fr : display_variant=A_MYEXT_X_PRIVATE
+disp_locale=de : display_variant=A_MYEXT_X_PRIVATE
+-----------------
+locale='en-a-myExt-b-another'
+disp_locale=en : display_variant=MYEXT_B_ANOTHER
+disp_locale=fr : display_variant=MYEXT_B_ANOTHER
+disp_locale=de : display_variant=MYEXT_B_ANOTHER
+-----------------
+locale='de-419-DE'
+disp_locale=en : display_variant=DE
+disp_locale=fr : display_variant=DE
+disp_locale=de : display_variant=DE
+-----------------
+locale='a-DE'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='ar-a-aaa-b-bbb-a-ccc'
+disp_locale=en : display_variant=AAA_B_BBB_A_CCC
+disp_locale=fr : display_variant=AAA_B_BBB_A_CCC
+disp_locale=de : display_variant=AAA_B_BBB_A_CCC
+-----------------
diff --git a/ext/intl/tests/locale_get_keywords.phpt b/ext/intl/tests/locale_get_keywords.phpt
new file mode 100755
index 0000000000..5dec0011e9
--- /dev/null
+++ b/ext/intl/tests/locale_get_keywords.phpt
@@ -0,0 +1,139 @@
+--TEST--
+locale_get_keywords()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the keywords for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $locales = array(
+ "de_DE@currency=EUR;collation=PHONEBOOK",
+ 'uk-ua_CALIFORNIA@currency=GRN'
+ );
+
+ $locales = array(
+ 'de_DE@currency=EUR;collation=PHONEBOOK',
+ '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 )
+ {
+ $keywords_arr = ut_loc_get_keywords( $locale);
+ $res_str .= "$locale: ";
+ if( $keywords_arr){
+ foreach( $keywords_arr as $key => $value){
+ $res_str .= "Key is $key and Value is $value \n";
+ }
+ }
+ else{
+ $res_str .= "No keywords found.";
+ }
+ $res_str .= "\n";
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+de_DE@currency=EUR;collation=PHONEBOOK: Key is collation and Value is PHONEBOOK
+Key is currency and Value is EUR
+
+root: No keywords found.
+uk@currency=EURO: Key is currency and Value is EURO
+
+Hindi: No keywords found.
+de: No keywords found.
+fr: No keywords found.
+ja: No keywords found.
+i-enochian: No keywords found.
+zh-Hant: No keywords found.
+zh-Hans: No keywords found.
+sr-Cyrl: No keywords found.
+sr-Latn: No keywords found.
+zh-Hans-CN: No keywords found.
+sr-Latn-CS: No keywords found.
+sl-rozaj: No keywords found.
+sl-nedis: No keywords found.
+de-CH-1901: No keywords found.
+sl-IT-nedis: No keywords found.
+sl-Latn-IT-nedis: No keywords found.
+de-DE: No keywords found.
+en-US: No keywords found.
+es-419: No keywords found.
+de-CH-x-phonebk: No keywords found.
+az-Arab-x-AZE-derbend: No keywords found.
+zh-min: No keywords found.
+zh-min-nan-Hant-CN: No keywords found.
+x-whatever: No keywords found.
+qaa-Qaaa-QM-x-southern: No keywords found.
+sr-Latn-QM: No keywords found.
+sr-Qaaa-CS: No keywords found.
+en-US-u-islamCal: No keywords found.
+zh-CN-a-myExt-x-private: No keywords found.
+en-a-myExt-b-another: No keywords found.
+de-419-DE: No keywords found.
+a-DE: No keywords found.
+ar-a-aaa-b-bbb-a-ccc: No keywords found.
diff --git a/ext/intl/tests/locale_get_primary_language.phpt b/ext/intl/tests/locale_get_primary_language.phpt
new file mode 100755
index 0000000000..bc92e9266c
--- /dev/null
+++ b/ext/intl/tests/locale_get_primary_language.phpt
@@ -0,0 +1,121 @@
+--TEST--
+locale_get_primary_language()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the prmary language for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $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
+ '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 )
+ {
+ $lang = ut_loc_get_primary_language( $locale);
+ $res_str .= "$locale: primary_language='$lang'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: primary_language='uk'
+root: primary_language='root'
+uk@currency=EURO: primary_language='uk'
+Hindi: primary_language='hindi'
+de: primary_language='de'
+fr: primary_language='fr'
+ja: primary_language='ja'
+i-enochian: primary_language='i-enochian'
+zh-Hant: primary_language='zh'
+zh-Hans: primary_language='zh'
+sr-Cyrl: primary_language='sr'
+sr-Latn: primary_language='sr'
+zh-Hans-CN: primary_language='zh'
+sr-Latn-CS: primary_language='sr'
+sl-rozaj: primary_language='sl'
+sl-nedis: primary_language='sl'
+de-CH-1901: primary_language='de'
+sl-IT-nedis: primary_language='sl'
+sl-Latn-IT-nedis: primary_language='sl'
+de-DE: primary_language='de'
+en-US: primary_language='en'
+es-419: primary_language='es'
+de-CH-x-phonebk: primary_language='de'
+az-Arab-x-AZE-derbend: primary_language='az'
+zh-min: primary_language='zh-min'
+zh-min-nan-Hant-CN: primary_language='zh'
+qaa-Qaaa-QM-x-southern: primary_language='qaa'
+sr-Latn-QM: primary_language='sr'
+sr-Qaaa-CS: primary_language='sr'
+en-US-u-islamCal: primary_language='en'
+zh-CN-a-myExt-x-private: primary_language='zh'
+en-a-myExt-b-another: primary_language='en'
+de-419-DE: primary_language='de'
+a-DE: primary_language='a'
+ar-a-aaa-b-bbb-a-ccc: primary_language='ar'
diff --git a/ext/intl/tests/locale_get_region.phpt b/ext/intl/tests/locale_get_region.phpt
new file mode 100755
index 0000000000..e62855e560
--- /dev/null
+++ b/ext/intl/tests/locale_get_region.phpt
@@ -0,0 +1,123 @@
+--TEST--
+locale_get_region()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the region for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $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 )
+ {
+ $scr = ut_loc_get_region( $locale);
+ $res_str .= "$locale: region='$scr'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: region='UA'
+root: region=''
+uk@currency=EURO: region=''
+Hindi: region=''
+de: region=''
+fr: region=''
+ja: region=''
+i-enochian: region=''
+zh-Hant: region=''
+zh-Hans: region=''
+sr-Cyrl: region=''
+sr-Latn: region=''
+zh-Hans-CN: region='CN'
+sr-Latn-CS: region='CS'
+sl-rozaj: region='ROZAJ'
+sl-nedis: region='NEDIS'
+de-CH-1901: region='CH'
+sl-IT-nedis: region='IT'
+sl-Latn-IT-nedis: region='IT'
+de-DE: region='DE'
+en-US: region='US'
+es-419: region='419'
+de-CH-x-phonebk: region='CH'
+az-Arab-x-AZE-derbend: region='X'
+zh-min: region=''
+zh-min-nan-Hant-CN: region='MIN'
+x-whatever: region=''
+qaa-Qaaa-QM-x-southern: region='QM'
+sr-Latn-QM: region='QM'
+sr-Qaaa-CS: region='CS'
+en-US-u-islamCal: region='US'
+zh-CN-a-myExt-x-private: region='CN'
+en-a-myExt-b-another: region='A'
+de-419-DE: region='419'
+a-DE: region='DE'
+ar-a-aaa-b-bbb-a-ccc: region='A'
diff --git a/ext/intl/tests/locale_get_script.phpt b/ext/intl/tests/locale_get_script.phpt
new file mode 100755
index 0000000000..6a794fdb12
--- /dev/null
+++ b/ext/intl/tests/locale_get_script.phpt
@@ -0,0 +1,122 @@
+--TEST--
+locale_get_script()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the prmary language for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+
+ $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 )
+ {
+ $scr = ut_loc_get_script( $locale);
+ $res_str .= "$locale: script='$scr'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: script=''
+root: script=''
+uk@currency=EURO: script=''
+Hindi: script=''
+de: script=''
+fr: script=''
+ja: script=''
+i-enochian: script=''
+zh-Hant: script='Hant'
+zh-Hans: script='Hans'
+sr-Cyrl: script='Cyrl'
+sr-Latn: script='Latn'
+zh-Hans-CN: script='Hans'
+sr-Latn-CS: script='Latn'
+sl-rozaj: script=''
+sl-nedis: script=''
+de-CH-1901: script=''
+sl-IT-nedis: script=''
+sl-Latn-IT-nedis: script='Latn'
+de-DE: script=''
+en-US: script=''
+es-419: script=''
+de-CH-x-phonebk: script=''
+az-Arab-x-AZE-derbend: script='Arab'
+zh-min: script=''
+zh-min-nan-Hant-CN: script=''
+x-whatever: script=''
+qaa-Qaaa-QM-x-southern: script='Qaaa'
+sr-Latn-QM: script='Latn'
+sr-Qaaa-CS: script='Qaaa'
+en-US-u-islamCal: script=''
+zh-CN-a-myExt-x-private: script=''
+en-a-myExt-b-another: script=''
+de-419-DE: script=''
+a-DE: script=''
+ar-a-aaa-b-bbb-a-ccc: script=''
+
diff --git a/ext/intl/tests/locale_lookup.phpt b/ext/intl/tests/locale_lookup.phpt
new file mode 100755
index 0000000000..08f92ffc41
--- /dev/null
+++ b/ext/intl/tests/locale_lookup.phpt
@@ -0,0 +1,99 @@
+--TEST--
+locale_lookup.phpt()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $loc_ranges = array(
+ 'de-de',
+ 'sl_IT',
+ 'sl_IT_Nedis',
+ 'jbo',
+ 'art-lojban'
+ );
+
+ $lang_tags = array(
+ 'de-DEVA',
+ 'de-DE-1996',
+ 'de-DE',
+ 'zh_Hans',
+ 'de-CH-1996',
+ 'sl_IT',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'sgn-CH-de',
+ 'art-lojban',
+ 'i-lux',
+ 'art-lojban',
+ 'jbo',
+ 'en_sl_IT',
+ 'zh-Hant-CN-x-prv1-prv2'
+ );
+
+
+ $res_str = '';
+ $isCanonical = false;
+
+ foreach($loc_ranges as $loc_range){
+ $res_str .="--------------\n";
+ $result= ut_loc_locale_lookup( $lang_tags , $loc_range,$isCanonical,"en_US");
+ $comma_arr =implode(",",$lang_tags);
+ $res_str .= "loc_range:$loc_range \nlang_tags: $comma_arr\n";
+ $res_str .= "\nlookup result:$result\n";
+//canonicalized version
+ $result= ut_loc_locale_lookup( $lang_tags , $loc_range,!($isCanonical),"en_US");
+ $can_loc_range = ut_loc_canonicalize($loc_range);
+ $res_str .= "Canonical lookup result:$result\n";
+
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+--------------
+loc_range:de-de
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:de_de
+Canonical lookup result:de_de
+--------------
+loc_range:sl_IT
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:sl_it
+Canonical lookup result:sl_it
+--------------
+loc_range:sl_IT_Nedis
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:sl_it
+Canonical lookup result:sl_it
+--------------
+loc_range:jbo
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:jbo
+Canonical lookup result:jbo
+--------------
+loc_range:art-lojban
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:art_lojban
+Canonical lookup result:jbo \ No newline at end of file
diff --git a/ext/intl/tests/locale_parse_locale.phpt b/ext/intl/tests/locale_parse_locale.phpt
new file mode 100755
index 0000000000..e670b31a13
--- /dev/null
+++ b/ext/intl/tests/locale_parse_locale.phpt
@@ -0,0 +1,201 @@
+--TEST--
+locale_parse_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $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
+ '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 )
+ {
+ $arr = ut_loc_locale_parse( $locale);
+ $res_str .= "---------------------\n";
+ $res_str .= "$locale: \n";
+ if( $arr){
+ foreach( $arr as $key => $value){
+ $res_str .= "$key : '$value' , ";
+ }
+ }
+ else{
+ $res_str .= "No values found from Locale parsing.";
+ }
+ $res_str .= "\n";
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+---------------------
+uk-ua_CALIFORNIA@currency=;currency=GRN:
+language : 'uk' , region : 'UA' , variant0 : 'CALIFORNIA' ,
+---------------------
+root:
+language : 'root' ,
+---------------------
+uk@currency=EURO:
+language : 'uk' ,
+---------------------
+Hindi:
+language : 'hindi' ,
+---------------------
+de:
+language : 'de' ,
+---------------------
+fr:
+language : 'fr' ,
+---------------------
+ja:
+language : 'ja' ,
+---------------------
+i-enochian:
+grandfathered : 'i-enochian' ,
+---------------------
+zh-Hant:
+language : 'zh' , script : 'Hant' ,
+---------------------
+zh-Hans:
+language : 'zh' , script : 'Hans' ,
+---------------------
+sr-Cyrl:
+language : 'sr' , script : 'Cyrl' ,
+---------------------
+sr-Latn:
+language : 'sr' , script : 'Latn' ,
+---------------------
+zh-Hans-CN:
+language : 'zh' , script : 'Hans' , region : 'CN' ,
+---------------------
+sr-Latn-CS:
+language : 'sr' , script : 'Latn' , region : 'CS' ,
+---------------------
+sl-rozaj:
+language : 'sl' , region : 'ROZAJ' ,
+---------------------
+sl-nedis:
+language : 'sl' , region : 'NEDIS' ,
+---------------------
+de-CH-1901:
+language : 'de' , region : 'CH' , variant0 : '1901' ,
+---------------------
+sl-IT-nedis:
+language : 'sl' , region : 'IT' , variant0 : 'NEDIS' ,
+---------------------
+sl-Latn-IT-nedis:
+language : 'sl' , script : 'Latn' , region : 'IT' , variant0 : 'NEDIS' ,
+---------------------
+de-DE:
+language : 'de' , region : 'DE' ,
+---------------------
+en-US:
+language : 'en' , region : 'US' ,
+---------------------
+es-419:
+language : 'es' , region : '419' ,
+---------------------
+de-CH-x-phonebk:
+language : 'de' , region : 'CH' , private0 : 'phonebk' ,
+---------------------
+az-Arab-x-AZE-derbend:
+language : 'az' , script : 'Arab' , private0 : 'AZE' , private1 : 'derbend' ,
+---------------------
+zh-min:
+grandfathered : 'zh-min' ,
+---------------------
+zh-min-nan-Hant-CN:
+language : 'zh' , region : 'MIN' , variant0 : 'NAN' , variant1 : 'HANT' , variant2 : 'CN' ,
+---------------------
+qaa-Qaaa-QM-x-southern:
+language : 'qaa' , script : 'Qaaa' , region : 'QM' , private0 : 'southern' ,
+---------------------
+sr-Latn-QM:
+language : 'sr' , script : 'Latn' , region : 'QM' ,
+---------------------
+sr-Qaaa-CS:
+language : 'sr' , script : 'Qaaa' , region : 'CS' ,
+---------------------
+en-US-u-islamCal:
+language : 'en' , region : 'US' ,
+---------------------
+zh-CN-a-myExt-x-private:
+language : 'zh' , region : 'CN' , private0 : 'private' ,
+---------------------
+en-a-myExt-b-another:
+language : 'en' ,
+---------------------
+de-419-DE:
+language : 'de' , region : '419' , variant0 : 'DE' ,
+---------------------
+a-DE:
+No values found from Locale parsing.
+---------------------
+ar-a-aaa-b-bbb-a-ccc:
+language : 'ar' ,
diff --git a/ext/intl/tests/locale_set_default.phpt b/ext/intl/tests/locale_set_default.phpt
new file mode 100755
index 0000000000..7debf4d3de
--- /dev/null
+++ b/ext/intl/tests/locale_set_default.phpt
@@ -0,0 +1,133 @@
+--TEST--
+locale_set_default($locale)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try setting the default Locale with different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $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'
+ );
+
+/*
+ $locales = array(
+ 'es'
+ );
+*/
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $isSuccessful = ut_loc_set_default( $locale);
+ if ($isSuccessful ){
+ $lang = ut_loc_get_default( );
+ $res_str .= "$locale: set locale '$lang'";
+ }
+ else{
+ $res_str .= "$locale: Error in set locale";
+ }
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: set locale 'uk-ua_CALIFORNIA@currency=;currency=GRN'
+root: set locale 'root'
+uk@currency=EURO: set locale 'uk@currency=EURO'
+Hindi: set locale 'Hindi'
+de: set locale 'de'
+fr: set locale 'fr'
+ja: set locale 'ja'
+i-enochian: set locale 'i-enochian'
+zh-Hant: set locale 'zh-Hant'
+zh-Hans: set locale 'zh-Hans'
+sr-Cyrl: set locale 'sr-Cyrl'
+sr-Latn: set locale 'sr-Latn'
+zh-Hans-CN: set locale 'zh-Hans-CN'
+sr-Latn-CS: set locale 'sr-Latn-CS'
+sl-rozaj: set locale 'sl-rozaj'
+sl-nedis: set locale 'sl-nedis'
+de-CH-1901: set locale 'de-CH-1901'
+sl-IT-nedis: set locale 'sl-IT-nedis'
+sl-Latn-IT-nedis: set locale 'sl-Latn-IT-nedis'
+de-DE: set locale 'de-DE'
+en-US: set locale 'en-US'
+es-419: set locale 'es-419'
+de-CH-x-phonebk: set locale 'de-CH-x-phonebk'
+az-Arab-x-AZE-derbend: set locale 'az-Arab-x-AZE-derbend'
+zh-min: set locale 'zh-min'
+zh-min-nan-Hant-CN: set locale 'zh-min-nan-Hant-CN'
+x-whatever: set locale 'x-whatever'
+qaa-Qaaa-QM-x-southern: set locale 'qaa-Qaaa-QM-x-southern'
+sr-Latn-QM: set locale 'sr-Latn-QM'
+sr-Qaaa-CS: set locale 'sr-Qaaa-CS'
+en-US-u-islamCal: set locale 'en-US-u-islamCal'
+zh-CN-a-myExt-x-private: set locale 'zh-CN-a-myExt-x-private'
+en-a-myExt-b-another: set locale 'en-a-myExt-b-another'
+de-419-DE: set locale 'de-419-DE'
+a-DE: set locale 'a-DE'
+ar-a-aaa-b-bbb-a-ccc: set locale 'ar-a-aaa-b-bbb-a-ccc'
diff --git a/ext/intl/tests/msgfmt_fail.phpt b/ext/intl/tests/msgfmt_fail.phpt
new file mode 100755
index 0000000000..f7d0276844
--- /dev/null
+++ b/ext/intl/tests/msgfmt_fail.phpt
@@ -0,0 +1,101 @@
+--TEST--
+msgfmt creation failures
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function err($fmt) {
+ if(!$fmt) {
+ echo var_export(intl_get_error_message(), true)."\n";
+ }
+}
+
+function crt($t, $l, $s) {
+ switch(true) {
+ case $t == "O":
+ return new MessageFormatter($l, $s);
+ break;
+ case $t == "C":
+ return MessageFormatter::create($l, $s);
+ break;
+ case $t == "P":
+ return msgfmt_create($l, $s);
+ break;
+ }
+}
+
+$args = array(
+ array(null, null),
+ array("whatever", "{0,whatever}"),
+ array(array(), array()),
+ array("en", "{0,choice}"),
+ array("fr", "{0,"),
+ array("en_US", "\xD0"),
+);
+
+$fmt = new MessageFormatter();
+err($fmt);
+$fmt = msgfmt_create();
+err($fmt);
+$fmt = MessageFormatter::create();
+err($fmt);
+$fmt = new MessageFormatter('en');
+err($fmt);
+$fmt = msgfmt_create('en');
+err($fmt);
+$fmt = MessageFormatter::create('en');
+err($fmt);
+
+foreach($args as $arg) {
+ $fmt = crt("O", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("C", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("P", $arg[0], $arg[1]);
+ err($fmt);
+}
+
+?>
+--EXPECTF--
+Warning: MessageFormatter::__construct() expects exactly 2 parameters, 0 given in %s on line %d
+'__construct: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: msgfmt_create() expects exactly 2 parameters, 0 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::create() expects exactly 2 parameters, 0 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::__construct() expects exactly 2 parameters, 1 given in %s on line %d
+'__construct: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: msgfmt_create() expects exactly 2 parameters, 1 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::create() expects exactly 2 parameters, 1 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::__construct() expects parameter 1 to be string, array given in %s on line %d
+'__construct: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: msgfmt_create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'__construct: message formatter creation failed: U_UNMATCHED_BRACES'
+'msgfmt_create: message formatter creation failed: U_UNMATCHED_BRACES'
+'msgfmt_create: message formatter creation failed: U_UNMATCHED_BRACES'
+'__construct: Error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND' \ No newline at end of file
diff --git a/ext/intl/tests/msgfmt_format.phpt b/ext/intl/tests/msgfmt_format.phpt
new file mode 100755
index 0000000000..c1bf5e9db6
--- /dev/null
+++ b/ext/intl/tests/msgfmt_format.phpt
@@ -0,0 +1,70 @@
+--TEST--
+msgfmt_format()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_US' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'ru_UA' => "{0,number,integer} мавп на {1,number,integer} деревах це {2,number} мавпи на кожному деревi",
+ 'de' => "{0,number,integer} Affen über {1,number,integer} Bäume um {2,number} Affen pro Baum",
+ 'en_UK' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'root' => '{0,whatever} would not work!',
+ 'fr' => "C'est la vie!",
+ );
+
+ $str_res = '';
+ $m = 4560;
+ $t = 123;
+
+ foreach( $locales as $locale => $pattern )
+ {
+ $str_res .= "\nLocale is: $locale\n";
+ $fmt = ut_msgfmt_create( $locale, $pattern );
+ if(!$fmt) {
+ $str_res .= dump(intl_get_error_message())."\n";
+ continue;
+ }
+ $str_res .= dump( ut_msgfmt_format( $fmt, array($m, $t, $m/$t) ) ) . "\n";
+ $str_res .= dump( ut_msgfmt_format_message($locale, $pattern, array($m, $t, $m/$t))) . "\n";
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Locale is: en_US
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+
+Locale is: ru_UA
+'4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi'
+'4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi'
+
+Locale is: de
+'4.560 Affen über 123 Bäume um 37,073 Affen pro Baum'
+'4.560 Affen über 123 Bäume um 37,073 Affen pro Baum'
+
+Locale is: en_UK
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+
+Locale is: root
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Locale is: fr
+'C\'est la vie!'
+'C\'est la vie!'
diff --git a/ext/intl/tests/msgfmt_get_error.phpt b/ext/intl/tests/msgfmt_get_error.phpt
new file mode 100755
index 0000000000..015c50d465
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_error.phpt
@@ -0,0 +1,29 @@
+--TEST--
+msgmfmt_get_error_message/code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Error handling.
+ */
+
+
+function ut_main()
+{
+ $fmt = ut_msgfmt_create( "en_US", "{0, number} monkeys on {1, number} trees" );
+ $num = ut_msgfmt_format( $fmt, array());
+ if( $num === false )
+ return $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n";
+ else
+ return "Ooops, an error should have occured.";
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+msgfmt_format: not enough parameters: U_ILLEGAL_ARGUMENT_ERROR (1)
diff --git a/ext/intl/tests/msgfmt_get_locale.phpt b/ext/intl/tests/msgfmt_get_locale.phpt
new file mode 100755
index 0000000000..4c2651fe2b
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_locale.phpt
@@ -0,0 +1,40 @@
+--TEST--
+msgfmt_get_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get locale.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK',
+ 'en_US@California',
+ 'uk',
+ );
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $fmt = ut_msgfmt_create( $locale, "Test" );
+ $res_str .= "$locale: " . dump( ut_msgfmt_get_locale( $fmt ) );
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+en_UK: 'en_UK'
+en_US@California: 'en_US@California'
+uk: 'uk'
diff --git a/ext/intl/tests/msgfmt_get_set_pattern.phpt b/ext/intl/tests/msgfmt_get_set_pattern.phpt
new file mode 100755
index 0000000000..dd6fb5fa2e
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_set_pattern.phpt
@@ -0,0 +1,53 @@
+--TEST--
+msgfmt_get/set_pattern()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set pattern.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+ $fmt = ut_msgfmt_create( "en_US", "{0,number} monkeys on {1,number} trees" );
+
+ // Get default patten.
+ $res_str .= "Default pattern: '" . ut_msgfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatting result: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+
+ // Set a new pattern.
+ $pattern = "{0,number} trees hosting {1,number} monkeys";
+ $res = ut_msgfmt_set_pattern( $fmt, $pattern );
+ if( $res === false )
+ $res_str .= ut_msgfmt_get_error_message( $fmt ) . " (" . ut_msgfmt_get_error_code( $fmt ) . ")\n";
+
+ // Check if the pattern has been changed.
+ $res = ut_msgfmt_get_pattern( $fmt );
+ if( $res === false )
+ $res_str .= ut_msgfmt_get_error_message( $fmt ) . " (" . ut_msgfmt_get_error_code( $fmt ) . ")\n";
+ $res_str .= "New pattern: '" . ut_msgfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+
+ ut_msgfmt_set_pattern($fmt, str_repeat($pattern, 10));
+ $res_str .= "New pattern: '" . ut_msgfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Default pattern: '{0,number} monkeys on {1,number} trees'
+Formatting result: 123 monkeys on 456 trees
+New pattern: '{0,number} trees hosting {1,number} monkeys'
+Formatted number: 123 trees hosting 456 monkeys
+New pattern: '{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys{0,number} trees hosting {1,number} monkeys'
+Formatted number: 123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys123 trees hosting 456 monkeys
diff --git a/ext/intl/tests/msgfmt_parse.phpt b/ext/intl/tests/msgfmt_parse.phpt
new file mode 100755
index 0000000000..b9ec36374b
--- /dev/null
+++ b/ext/intl/tests/msgfmt_parse.phpt
@@ -0,0 +1,114 @@
+--TEST--
+msgfmt_parse() tests
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_US' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'ru_UA' => "{0,number,integer} мавп на {1,number,integer} деревах це {2,number} мавпи на кожному деревi",
+ 'de' => "{0,number,integer} Affen über {1,number,integer} Bäume um {2,number} Affen pro Baum",
+ 'en_UK' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'root' => '{0,whatever} would not work!',
+ 'fr' => 'C\'est {0,number,integer}',
+ );
+
+ $results = array(
+ 'en_US' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'ru_UA' => "4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi",
+ 'de' => "4.560 Affen über 123 Bäume um 37,073 Affen pro Baum",
+ 'en_UK' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'root' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'fr' => "C'est 42",
+
+ );
+
+ $str_res = '';
+
+ foreach( $locales as $locale => $pattern )
+ {
+ $str_res .= "\nLocale is: $locale\n";
+ $fmt = ut_msgfmt_create( $locale, $pattern );
+ if(!$fmt) {
+ $str_res .= dump(intl_get_error_message())."\n";
+ continue;
+ }
+ $str_res .= dump( ut_msgfmt_parse( $fmt, $results[$locale] ) ) . "\n";
+ $str_res .= dump( ut_msgfmt_parse_message($locale, $pattern, $results[$locale])) . "\n";
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Locale is: en_US
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: ru_UA
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: de
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: en_UK
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: root
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Locale is: fr
+array (
+ 0 => 42,
+)
+array (
+ 0 => 42,
+)
diff --git a/ext/intl/tests/normalizer_normalize.phpt b/ext/intl/tests/normalizer_normalize.phpt
new file mode 100755
index 0000000000..238b7423cf
--- /dev/null
+++ b/ext/intl/tests/normalizer_normalize.phpt
@@ -0,0 +1,160 @@
+--TEST--
+normalize()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try normalization and test normalization
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $forms = array(
+ Normalizer::FORM_C,
+ Normalizer::FORM_D,
+ Normalizer::FORM_KC,
+ Normalizer::FORM_KD,
+ Normalizer::NONE,
+ );
+
+ $forms_str = array (
+ Normalizer::FORM_C => 'UNORM_FORM_C',
+ Normalizer::FORM_D => 'UNORM_FORM_D',
+ Normalizer::FORM_KC => 'UNORM_FORM_KC',
+ Normalizer::FORM_KD => 'UNORM_FORM_KD',
+ Normalizer::NONE => 'UNORM_NONE',
+ );
+
+ /* just make sure all the form constants are defined as in the api spec */
+ if ( Normalizer::FORM_C != Normalizer::NFC ||
+ Normalizer::FORM_D != Normalizer::NFD ||
+ Normalizer::FORM_KC != Normalizer::NFKC ||
+ Normalizer::FORM_KD != Normalizer::NFKD ||
+ Normalizer::NONE == Normalizer::FORM_C ) {
+
+ $res_str .= "Invalid normalization form declarations!\n";
+ }
+
+ $char_a_diaeresis = "\xC3\xA4"; // 'LATIN SMALL LETTER A WITH DIAERESIS' (U+00E4)
+ $char_a_ring = "\xC3\xA5"; // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
+ $char_o_diaeresis = "\xC3\xB6"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
+
+ $char_angstrom_sign = "\xE2\x84\xAB"; // 'ANGSTROM SIGN' (U+212B)
+ $char_A_ring = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
+
+ $char_ohm_sign = "\xE2\x84\xA6"; // 'OHM SIGN' (U+2126)
+ $char_omega = "\xCE\xA9"; // 'GREEK CAPITAL LETTER OMEGA' (U+03A9)
+
+ $char_combining_ring_above = "\xCC\x8A"; // 'COMBINING RING ABOVE' (U+030A)
+
+ $char_fi_ligature = "\xEF\xAC\x81"; // 'LATIN SMALL LIGATURE FI' (U+FB01)
+
+ $char_long_s_dot = "\xE1\xBA\x9B"; // 'LATIN SMALL LETTER LONG S WITH DOT ABOVE' (U+1E9B)
+
+ $strs = array(
+ 'ABC',
+ $char_a_diaeresis . '||' . $char_a_ring . '||' . $char_o_diaeresis,
+ $char_angstrom_sign . '||' . $char_A_ring . '||' . 'A' . $char_combining_ring_above,
+ $char_ohm_sign . '||' . $char_omega,
+ $char_fi_ligature,
+ $char_long_s_dot,
+ );
+
+ foreach( $forms as $form )
+ {
+ foreach( $strs as $str )
+ {
+ $str_norm = ut_norm_normalize( $str, $form );
+ $error_code = intl_get_error_code();
+ $error_message = intl_get_error_message();
+
+ $str_hex = urlencode($str);
+ $str_norm_hex = urlencode($str_norm);
+ $res_str .= "'$str_hex' normalized to form '{$forms_str[$form]}' is '$str_norm_hex'"
+ . "\terror info: '$error_message' ($error_code)\n"
+ . "";
+
+ $is_norm = ut_norm_is_normalized( $str, $form );
+ $error_code = intl_get_error_code();
+ $error_message = intl_get_error_message();
+
+ $res_str .= " is in form '{$forms_str[$form]}'? = " . ($is_norm ? "yes" : "no")
+ . "\terror info: '$error_message' ($error_code)\n"
+ . "";
+ }
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+'ABC' normalized to form 'UNORM_FORM_C' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_C' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_C' is '%C3%85%7C%7C%C3%85%7C%7C%C3%85' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_C' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_C' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_C' is '%E1%BA%9B' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_D' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_D' is 'a%CC%88%7C%7Ca%CC%8A%7C%7Co%CC%88' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_D' is 'A%CC%8A%7C%7CA%CC%8A%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_D' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_D' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_D' is '%C5%BF%CC%87' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_KC' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_KC' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_KC' is '%C3%85%7C%7C%C3%85%7C%7C%C3%85' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_KC' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_KC' is 'fi' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_KC' is '%E1%B9%A1' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_KD' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_KD' is 'a%CC%88%7C%7Ca%CC%8A%7C%7Co%CC%88' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_KD' is 'A%CC%8A%7C%7CA%CC%8A%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_KD' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_KD' is 'fi' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_KD' is 's%CC%87' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_NONE' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_NONE' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_NONE' is '%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_NONE' is '%E2%84%A6%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%EF%AC%81' normalized to form 'UNORM_NONE' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E1%BA%9B' normalized to form 'UNORM_NONE' is '%E1%BA%9B' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+
diff --git a/ext/intl/tests/regression_sort_and_cow.phpt b/ext/intl/tests/regression_sort_and_cow.phpt
new file mode 100755
index 0000000000..d7ff797705
--- /dev/null
+++ b/ext/intl/tests/regression_sort_and_cow.phpt
@@ -0,0 +1,85 @@
+--TEST--
+Regression: sort() and copy-on-write.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check if collator_sort() properly supports copy-on-write.
+ */
+
+
+/* Create two copies of the given array.
+ * Sort the array and the first copy.
+ * Check if the second copy remains unsorted.
+ */
+function test_COW( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ // Create two copies of the given array.
+ $copy1 = $test_array;
+ $copy2 = $test_array;
+
+ // Sort given array and the first copy of it.
+ ut_coll_sort( $coll, $test_array );
+ ut_coll_sort( $coll, $copy1 );
+
+ // Return contents of all the arrays.
+ // The second copy should remain unsorted.
+ $res_str .= dump( $test_array ) . "\n";
+ $res_str .= dump( $copy1 ) . "\n";
+ $res_str .= dump( $copy2 ) . "\n";
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ $a1 = array( 'b', 'a', 'c' );
+ $a2 = array( 'в', 'а', 'б' );
+
+ $res_str .= test_COW( 'en_US', $a1 );
+ $res_str .= test_COW( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'b',
+ 1 => 'a',
+ 2 => 'c',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'в',
+ 1 => 'а',
+ 2 => 'б',
+)
diff --git a/ext/intl/tests/regression_sort_eq.phpt b/ext/intl/tests/regression_sort_eq.phpt
new file mode 100755
index 0000000000..8676c5c931
--- /dev/null
+++ b/ext/intl/tests/regression_sort_eq.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Regression: sort() eq but different len.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Test sorting strings that have different length but otherwise equal.
+ */
+
+function sort_using_locale( $locale, $test_array )
+{
+ $coll = ut_coll_create( $locale );
+
+ // Sort array.
+ ut_coll_sort( $coll, $test_array );
+
+ // And return the sorted array.
+ return dump( $test_array ) . "\n";
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Define a couple of arrays.
+ // Each array contains equal strings that differ only in their length.
+ $a1 = array( 'aa', 'aaa', 'a' );
+ $a2 = array( 'пп', 'ппп', 'п' );
+
+ // Sort them.
+ $res_str .= sort_using_locale( 'en_US', $a1 );
+ $res_str .= sort_using_locale( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+array (
+ 0 => 'п',
+ 1 => 'пп',
+ 2 => 'ппп',
+)
diff --git a/ext/intl/tests/regression_sortwsk_and_cow.phpt b/ext/intl/tests/regression_sortwsk_and_cow.phpt
new file mode 100755
index 0000000000..a4953232ef
--- /dev/null
+++ b/ext/intl/tests/regression_sortwsk_and_cow.phpt
@@ -0,0 +1,86 @@
+--TEST--
+Regression: sort_wsk() and copy-on-write.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check if collator_sort_with_sort_keys()
+ * properly supports copy-on-write.
+ */
+
+
+/* Create two copies of the given array.
+ * Sort the array and the first copy.
+ * Check if the second copy remains unsorted.
+ */
+function test_COW( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ // Create two copies of the given array.
+ $copy1 = $test_array;
+ $copy2 = $test_array;
+
+ // Sort given array and the first copy of it.
+ ut_coll_sort_with_sort_keys( $coll, $test_array );
+ ut_coll_sort_with_sort_keys( $coll, $copy1 );
+
+ // Return contents of all the arrays.
+ // The second copy should remain unsorted.
+ $res_str .= dump( $test_array ) . "\n";
+ $res_str .= dump( $copy1 ) . "\n";
+ $res_str .= dump( $copy2 ) . "\n";
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ $a1 = array( 'b', 'a', 'c' );
+ $a2 = array( 'в', 'а', 'б' );
+
+ $res_str .= test_COW( 'en_US', $a1 );
+ $res_str .= test_COW( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'b',
+ 1 => 'a',
+ 2 => 'c',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'в',
+ 1 => 'а',
+ 2 => 'б',
+)
diff --git a/ext/intl/tests/regression_sortwsk_eq.phpt b/ext/intl/tests/regression_sortwsk_eq.phpt
new file mode 100755
index 0000000000..945bac300a
--- /dev/null
+++ b/ext/intl/tests/regression_sortwsk_eq.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Regression: sort_wsk() eq but different len.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Test sorting strings that have different length but otherwise equal.
+ */
+
+function sort_using_locale( $locale, $test_array )
+{
+ $coll = ut_coll_create( $locale );
+
+ // Sort array.
+ ut_coll_sort_with_sort_keys( $coll, $test_array );
+
+ // And return the sorted array.
+ return dump( $test_array ) . "\n";
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Define a couple of arrays.
+ // Each array contains equal strings that differ only in their length.
+ $a1 = array( 'aa', 'aaa', 'a' );
+ $a2 = array( 'пп', 'ппп', 'п' );
+
+ // Sort them.
+ $res_str .= sort_using_locale( 'en_US', $a1 );
+ $res_str .= sort_using_locale( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+array (
+ 0 => 'п',
+ 1 => 'пп',
+ 2 => 'ппп',
+)
diff --git a/ext/intl/tests/ut_common.inc b/ext/intl/tests/ut_common.inc
new file mode 100755
index 0000000000..9cc34b374a
--- /dev/null
+++ b/ext/intl/tests/ut_common.inc
@@ -0,0 +1,385 @@
+<?php
+/*
+ * Run unit test in OO- and in procedural mode.
+ * Then compare the outputs.
+ * It they're equal then show one of them.
+ * Otherwise indicate an error.
+ */
+function ut_run()
+{
+ // Run unit test in OO mode.
+ $GLOBALS['oo-mode'] = true;
+ $oo_result = ut_main();
+
+ // Run unit test in procedural mode.
+ $GLOBALS['oo-mode'] = false;
+ $proc_result = ut_main();
+
+ // Show error if the APIs produce different results.
+ if( $proc_result !== $oo_result )
+ {
+ echo "ERROR: OO- and procedural APIs produce different results!\n";
+ echo "OO API output:\n";
+ echo str_repeat( '=', 78 ) . "\n";
+ echo $oo_result;
+ echo str_repeat( '=', 78 ) . "\n";
+ echo "procedural API output:\n";
+ echo str_repeat( '=', 78 ) . "\n";
+ echo $proc_result;
+ echo str_repeat( '=', 78 ) . "\n";
+ return;
+ }
+
+ // Else, if the results are equal, show one of them.
+ echo $proc_result;
+}
+
+function dump( $val )
+{
+ return var_export( $val, true );
+}
+
+/*
+ * Wrappers around Collator methods to run them in either OO- or procedural mode.
+ */
+
+function ut_coll_create( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Collator::create( $locale ) : collator_create( $locale );
+}
+function ut_coll_compare( $coll, $str1, $str2 )
+{
+ return $GLOBALS['oo-mode'] ? $coll->compare( $str1, $str2 ) : collator_compare( $coll, $str1, $str2 );
+}
+function ut_coll_sort( $coll, &$arr, $sort_flag = Collator::SORT_REGULAR )
+{
+ return $GLOBALS['oo-mode'] ? $coll->sort( $arr, $sort_flag ) : collator_sort( $coll, $arr, $sort_flag );
+}
+function ut_coll_sort_with_sort_keys( $coll, &$arr )
+{
+ return $GLOBALS['oo-mode'] ? $coll->sortWithSortKeys( $arr ) : collator_sort_with_sort_keys( $coll, $arr );
+}
+function ut_coll_asort( $coll, &$arr, $sort_flag = Collator::SORT_REGULAR )
+{
+ return $GLOBALS['oo-mode'] ? $coll->asort( $arr, $sort_flag ) : collator_asort( $coll, $arr, $sort_flag );
+}
+function ut_coll_get_locale( $coll, $type )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getLocale( $type ) : collator_get_locale( $coll, $type );
+}
+function ut_coll_get_display_name( $obj_loc, $disp_loc )
+{
+ return $GLOBALS['oo-mode'] ? Collator::getDisplayName( $obj_loc, $disp_loc ) : collator_get_display_name( $obj_loc, $disp_loc );
+}
+function ut_coll_get_available_locales()
+{
+ return $GLOBALS['oo-mode'] ? Collator::getAvailableLocales() : collator_get_available_locales();
+}
+function ut_coll_get_attribute( $coll, $attr )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getAttribute( $attr ) : collator_get_attribute( $coll, $attr );
+}
+function ut_coll_get_strength( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getStrength() : collator_get_strength( $coll );
+}
+function ut_coll_set_strength( $coll, $strength )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setStrength( $strength ) : collator_set_strength( $coll, $strength );
+}
+function ut_coll_set_attribute( $coll, $attr, $val )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setAttribute( $attr, $val ) : collator_set_attribute( $coll, $attr, $val );
+}
+function ut_coll_get_variable_top( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getVariableTop() : collator_get_variable_top( $coll );
+}
+function ut_coll_set_variable_top( $coll, $var_top )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setVariableTop( $var_top ) : collator_set_variable_top( $coll, $var_top );
+}
+function ut_coll_restore_variable_top( $coll, $var_top )
+{
+ return $GLOBALS['oo-mode'] ? $coll->restoreVariableTop( $var_top ) : collator_restore_variable_top( $coll, $var_top );
+}
+function ut_coll_get_error_code( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getErrorCode() : collator_get_error_code( $coll );
+}
+function ut_coll_get_error_message( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getErrorMessage() : collator_get_error_message( $coll );
+}
+function ut_coll_get_default()
+{
+ return $GLOBALS['oo-mode'] ? Collator::getDefault() : collator_get_default();
+}
+function ut_coll_set_default( $coll )
+{
+ return $GLOBALS['oo-mode'] ? Collator::setDefault( $coll ) : collator_set_default( $coll );
+}
+
+/*
+ * Wrappers around NumberFormatter methods to run them in either OO- or procedural mode.
+ */
+
+// FIXME: incomplete list
+
+function ut_nfmt_create( $locale, $style, $pattern = null )
+{
+ return $GLOBALS['oo-mode'] ? new NumberFormatter( $locale, $style, $pattern ) : numfmt_create( $locale, $style, $pattern );
+}
+function ut_nfmt_format( $fmt, $number, $type = null )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $number, $type ) : numfmt_format( $fmt, $number, $type );
+}
+function ut_nfmt_parse( $fmt, $string, $type = NumberFormatter::TYPE_DOUBLE, &$position = null )
+{
+ if(is_null($position)) {
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string, $type ) : numfmt_parse( $fmt, $string, $type );
+ } else {
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string, $type, $position ) : numfmt_parse( $fmt, $string, $type, $position );
+ }
+}
+function ut_nfmt_format_currency( $fmt, $number, $currency )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->formatCurrency( $number, $currency ) : numfmt_format_currency( $fmt, $number, $currency );
+}
+function ut_nfmt_parse_currency( $fmt, $string, &$currency, &$position = null )
+{
+ if(is_null($position)) {
+ return $GLOBALS['oo-mode'] ? $fmt->parseCurrency( $string, $currency ) : numfmt_parse_currency( $fmt, $string, $currency );
+ } else {
+ return $GLOBALS['oo-mode'] ? $fmt->parseCurrency( $string, $currency, $position ) : numfmt_parse_currency( $fmt, $string, $currency, $position );
+ }
+}
+function ut_nfmt_set_attribute( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setAttribute( $attribute, $value ) : numfmt_set_attribute( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_text_attribute( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setTextAttribute( $attribute, $value ) : numfmt_set_text_attribute( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_symbol( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setSymbol( $attribute, $value ) : numfmt_set_symbol( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_pattern( $fmt, $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : numfmt_set_pattern( $fmt, $pattern );
+}
+function ut_nfmt_get_attribute( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getAttribute( $attribute ) : numfmt_get_attribute( $fmt, $attribute );
+}
+function ut_nfmt_get_text_attribute( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTextAttribute( $attribute ) : numfmt_get_text_attribute( $fmt, $attribute );
+}
+function ut_nfmt_get_symbol( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getSymbol( $attribute ) : numfmt_get_symbol( $fmt, $attribute );
+}
+function ut_nfmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : numfmt_get_pattern( $fmt );
+}
+function ut_nfmt_get_locale( $fmt, $type = 0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale( $type ) : numfmt_get_locale( $fmt, $type );
+}
+function ut_nfmt_get_error_code( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorCode() : numfmt_get_error_code( $fmt );
+}
+function ut_nfmt_get_error_message( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorMessage() : numfmt_get_error_message( $fmt );
+}
+
+function ut_norm_normalize( $str, $form )
+{
+ return $GLOBALS['oo-mode'] ? Normalizer::normalize( $str, $form ) : normalizer_normalize( $str, $form );
+}
+function ut_norm_is_normalized( $str, $form )
+{
+ return $GLOBALS['oo-mode'] ? Normalizer::isNormalized( $str, $form ) : normalizer_is_normalized( $str, $form );
+}
+
+/*
+ * Wrappers around Collator methods to run them in either OO- or procedural mode.
+ */
+
+function ut_loc_get_default( )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDefault( ) : locale_get_default();
+}
+function ut_loc_set_default( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::setDefault( $locale ) : locale_set_default( $locale );
+}
+function ut_loc_get_primary_language( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getPrimaryLanguage( $locale ) : locale_get_primary_language( $locale );
+}
+function ut_loc_get_script( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getScript( $locale ) : locale_get_script( $locale );
+}
+function ut_loc_get_region( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getRegion( $locale ) : locale_get_region( $locale );
+}
+function ut_loc_get_keywords( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getKeywords( $locale ) : locale_get_keywords( $locale );
+}
+function ut_loc_get_display_name( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayName( $locale , $dispLocale ) : locale_get_display_name( $locale , $dispLocale );
+}
+function ut_loc_get_display_language( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayLanguage( $locale , $dispLocale ) : locale_get_display_language( $locale , $dispLocale );
+}
+function ut_loc_get_display_script( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayScript( $locale , $dispLocale ) : locale_get_display_script( $locale , $dispLocale );
+}
+function ut_loc_get_display_region( $locale, $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayRegion( $locale, $dispLocale ) : locale_get_display_region( $locale, $dispLocale );
+}
+function ut_loc_get_display_variant( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayVariant( $locale , $dispLocale ) : locale_get_display_variant( $locale, $dispLocale );
+}
+function ut_loc_locale_compose( $loc_parts_arr )
+{
+ return $GLOBALS['oo-mode'] ? Locale::composeLocale( $loc_parts_arr ) : locale_compose( $loc_parts_arr );
+}
+function ut_loc_locale_parse( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::parseLocale( $locale ) : locale_parse($locale );
+}
+function ut_loc_locale_get_all_variants( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getAllVariants( $locale ) : locale_get_all_variants( $locale );
+}
+function ut_loc_locale_filter_matches( $lang_tag,$loc_range ,$isCanonical)
+{
+ return $GLOBALS['oo-mode'] ? Locale::filterMatches( $lang_tag,$loc_range ,$isCanonical) : locale_filter_matches( $lang_tag,$loc_range ,$isCanonical);
+}
+function ut_loc_canonicalize( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::canonicalize( $locale ) : locale_canonicalize( $locale );
+}
+function ut_loc_locale_lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc)
+{
+ return $GLOBALS['oo-mode'] ? Locale::lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc ) : locale_lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc );
+}
+/* MessageFormatter functions */
+function ut_msgfmt_create( $locale, $pattern)
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::create( $locale, $pattern ) : msgfmt_create( $locale, $pattern );
+}
+function ut_msgfmt_format( $fmt, $args )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $args ) : msgfmt_format( $fmt, $args);
+}
+function ut_msgfmt_parse( $fmt, $string)
+{
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string) : msgfmt_parse( $fmt, $string);
+}
+function ut_msgfmt_format_message( $locale, $pattern, $args )
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::formatMessage( $locale, $pattern, $args ) : msgfmt_format_message( $locale, $pattern, $args );
+}
+function ut_msgfmt_parse_message( $locale, $pattern, $string )
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::parseMessage( $locale, $pattern, $string ) : msgfmt_parse_message( $locale, $pattern, $string );
+}
+function ut_msgfmt_set_pattern( $fmt, $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : msgfmt_set_pattern( $fmt, $pattern );
+}
+function ut_msgfmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : msgfmt_get_pattern( $fmt );
+}
+function ut_msgfmt_get_locale( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale( ) : msgfmt_get_locale( $fmt );
+}
+function ut_msgfmt_get_error_code( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorCode() : msgfmt_get_error_code( $fmt );
+}
+function ut_msgfmt_get_error_message( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorMessage() : msgfmt_get_error_message( $fmt );
+}
+/* IntlDateFormatter functions */
+function ut_datefmt_create( $locale, $datetype, $timetype, $timezone = null, $calendar = null ,$pattern = null)
+{
+ return $GLOBALS['oo-mode'] ? datefmt_create( $locale, $datetype, $timetype, $timezone, $calendar ,$pattern ) : datefmt_create( $locale, $datetype, $timetype, $timezone, $calendar ,$pattern);
+}
+function ut_datefmt_get_datetype( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getDateType( ) : datefmt_get_datetype( $fmt );
+}
+function ut_datefmt_get_timetype( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTimeType( ) : datefmt_get_timetype( $fmt );
+}
+function ut_datefmt_get_calendar( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getCalendar( ) : datefmt_get_calendar( $fmt );
+}
+function ut_datefmt_set_calendar( $fmt ,$calendar )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setCalendar( $calendar ) : datefmt_set_calendar( $fmt , $calendar );
+}
+function ut_datefmt_get_timezone_id( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTimeZoneId( ) : datefmt_get_timezone_id( $fmt );
+}
+function ut_datefmt_set_timezone_id( $fmt ,$timezone_id )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setTimeZoneId( $timezone_id ) : datefmt_set_timezone_id( $fmt ,$timezone_id);
+}
+function ut_datefmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : datefmt_get_pattern( $fmt );
+}
+function ut_datefmt_set_pattern( $fmt , $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : datefmt_set_pattern( $fmt , $pattern);
+}
+function ut_datefmt_get_locale( $fmt ,$type=ULOC_ACTUAL_LOCALE)
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale($type ) : datefmt_get_locale( $fmt ,$type);
+}
+function ut_datefmt_is_lenient( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->isLenient() : datefmt_is_lenient( $fmt );
+}
+function ut_datefmt_set_lenient( $fmt , $lenient )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setLenient( $lenient ) : datefmt_set_lenient( $fmt , $lenient);
+}
+function ut_datefmt_format( $fmt , $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $value ) : datefmt_format( $fmt , $value);
+}
+function ut_datefmt_parse( $fmt , $value ,$parse_pos=0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $value ,$parse_pos ) : datefmt_parse( $fmt , $value,$parse_pos);
+}
+function ut_datefmt_localtime( $fmt , $value ,$parse_pos=0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->localtime( $value , $parse_pos ) : datefmt_localtime( $fmt , $value , $parse_pos );
+}
+?>