]> granicus.if.org Git - php/commitdiff
Merge intl extension to HEAD
authorStanislav Malyshev <stas@php.net>
Mon, 7 Jul 2008 23:42:30 +0000 (23:42 +0000)
committerStanislav Malyshev <stas@php.net>
Mon, 7 Jul 2008 23:42:30 +0000 (23:42 +0000)
# do not use yet in HEAD, needs some adjustment

171 files changed:
ext/intl/CREDITS [new file with mode: 0755]
ext/intl/TODO [new file with mode: 0755]
ext/intl/collator/collator.c [new file with mode: 0755]
ext/intl/collator/collator.h [new file with mode: 0755]
ext/intl/collator/collator_attr.c [new file with mode: 0755]
ext/intl/collator/collator_attr.h [new file with mode: 0755]
ext/intl/collator/collator_class.c [new file with mode: 0755]
ext/intl/collator/collator_class.h [new file with mode: 0755]
ext/intl/collator/collator_compare.c [new file with mode: 0755]
ext/intl/collator/collator_compare.h [new file with mode: 0755]
ext/intl/collator/collator_convert.c [new file with mode: 0755]
ext/intl/collator/collator_convert.h [new file with mode: 0755]
ext/intl/collator/collator_create.c [new file with mode: 0755]
ext/intl/collator/collator_create.h [new file with mode: 0755]
ext/intl/collator/collator_error.c [new file with mode: 0755]
ext/intl/collator/collator_error.h [new file with mode: 0755]
ext/intl/collator/collator_is_numeric.c [new file with mode: 0755]
ext/intl/collator/collator_is_numeric.h [new file with mode: 0755]
ext/intl/collator/collator_locale.c [new file with mode: 0755]
ext/intl/collator/collator_locale.h [new file with mode: 0755]
ext/intl/collator/collator_sort.c [new file with mode: 0755]
ext/intl/collator/collator_sort.h [new file with mode: 0755]
ext/intl/common/common_error.c [new file with mode: 0755]
ext/intl/common/common_error.h [new file with mode: 0755]
ext/intl/config.m4 [new file with mode: 0755]
ext/intl/config.w32 [new file with mode: 0755]
ext/intl/dateformat/dateformat.c [new file with mode: 0755]
ext/intl/dateformat/dateformat.h [new file with mode: 0755]
ext/intl/dateformat/dateformat_attr.c [new file with mode: 0755]
ext/intl/dateformat/dateformat_attr.h [new file with mode: 0755]
ext/intl/dateformat/dateformat_class.c [new file with mode: 0755]
ext/intl/dateformat/dateformat_class.h [new file with mode: 0755]
ext/intl/dateformat/dateformat_data.c [new file with mode: 0755]
ext/intl/dateformat/dateformat_data.h [new file with mode: 0755]
ext/intl/dateformat/dateformat_format.c [new file with mode: 0755]
ext/intl/dateformat/dateformat_format.h [new file with mode: 0755]
ext/intl/dateformat/dateformat_parse.c [new file with mode: 0755]
ext/intl/dateformat/dateformat_parse.h [new file with mode: 0755]
ext/intl/doc/Tutorial.txt [new file with mode: 0755]
ext/intl/doc/collator_api.php [new file with mode: 0755]
ext/intl/doc/common_api.php [new file with mode: 0755]
ext/intl/doc/datefmt_api.php [new file with mode: 0755]
ext/intl/doc/formatter_api.php [new file with mode: 0755]
ext/intl/doc/grapheme_api.php [new file with mode: 0755]
ext/intl/doc/locale_api.php [new file with mode: 0755]
ext/intl/doc/msgfmt_api.php [new file with mode: 0755]
ext/intl/doc/normalizer_api.php [new file with mode: 0755]
ext/intl/formatter/formatter.c [new file with mode: 0755]
ext/intl/formatter/formatter.h [new file with mode: 0755]
ext/intl/formatter/formatter_attr.c [new file with mode: 0755]
ext/intl/formatter/formatter_attr.h [new file with mode: 0755]
ext/intl/formatter/formatter_class.c [new file with mode: 0755]
ext/intl/formatter/formatter_class.h [new file with mode: 0755]
ext/intl/formatter/formatter_data.c [new file with mode: 0755]
ext/intl/formatter/formatter_data.h [new file with mode: 0755]
ext/intl/formatter/formatter_format.c [new file with mode: 0755]
ext/intl/formatter/formatter_format.h [new file with mode: 0755]
ext/intl/formatter/formatter_main.c [new file with mode: 0755]
ext/intl/formatter/formatter_main.c.orig [new file with mode: 0755]
ext/intl/formatter/formatter_main.h [new file with mode: 0755]
ext/intl/formatter/formatter_parse.c [new file with mode: 0755]
ext/intl/formatter/formatter_parse.h [new file with mode: 0755]
ext/intl/grapheme/grapheme.h [new file with mode: 0755]
ext/intl/grapheme/grapheme_string.c [new file with mode: 0755]
ext/intl/grapheme/grapheme_util.c [new file with mode: 0755]
ext/intl/grapheme/grapheme_util.h [new file with mode: 0755]
ext/intl/intl_common.h [new file with mode: 0755]
ext/intl/intl_convert.c [new file with mode: 0755]
ext/intl/intl_convert.h [new file with mode: 0755]
ext/intl/intl_data.h [new file with mode: 0755]
ext/intl/intl_error.c [new file with mode: 0755]
ext/intl/intl_error.h [new file with mode: 0755]
ext/intl/locale/locale.c [new file with mode: 0755]
ext/intl/locale/locale.h [new file with mode: 0755]
ext/intl/locale/locale_class.c [new file with mode: 0755]
ext/intl/locale/locale_class.h [new file with mode: 0755]
ext/intl/locale/locale_methods.c [new file with mode: 0755]
ext/intl/locale/locale_methods.h [new file with mode: 0755]
ext/intl/msgformat/msgformat.c [new file with mode: 0755]
ext/intl/msgformat/msgformat.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_attr.c [new file with mode: 0755]
ext/intl/msgformat/msgformat_attr.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_class.c [new file with mode: 0755]
ext/intl/msgformat/msgformat_class.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_data.c [new file with mode: 0755]
ext/intl/msgformat/msgformat_data.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_format.c [new file with mode: 0755]
ext/intl/msgformat/msgformat_format.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_helpers.cpp [new file with mode: 0755]
ext/intl/msgformat/msgformat_helpers.h [new file with mode: 0755]
ext/intl/msgformat/msgformat_parse.c [new file with mode: 0755]
ext/intl/msgformat/msgformat_parse.h [new file with mode: 0755]
ext/intl/normalizer/normalizer.c [new file with mode: 0755]
ext/intl/normalizer/normalizer.h [new file with mode: 0755]
ext/intl/normalizer/normalizer_class.c [new file with mode: 0755]
ext/intl/normalizer/normalizer_class.h [new file with mode: 0755]
ext/intl/normalizer/normalizer_normalize.c [new file with mode: 0755]
ext/intl/normalizer/normalizer_normalize.h [new file with mode: 0755]
ext/intl/php_intl.c [new file with mode: 0755]
ext/intl/php_intl.h [new file with mode: 0755]
ext/intl/tests/bug12887.phpt [new file with mode: 0755]
ext/intl/tests/collation_customization.phpt [new file with mode: 0755]
ext/intl/tests/collator_asort.phpt [new file with mode: 0755]
ext/intl/tests/collator_compare.phpt [new file with mode: 0755]
ext/intl/tests/collator_create.phpt [new file with mode: 0755]
ext/intl/tests/collator_get_error_code.phpt [new file with mode: 0755]
ext/intl/tests/collator_get_error_message.phpt [new file with mode: 0755]
ext/intl/tests/collator_get_locale.phpt [new file with mode: 0755]
ext/intl/tests/collator_get_set_attribute.phpt [new file with mode: 0755]
ext/intl/tests/collator_get_set_strength.phpt [new file with mode: 0755]
ext/intl/tests/collator_sort.phpt [new file with mode: 0755]
ext/intl/tests/collator_sort_with_sort_keys.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_format.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_format_parse.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_datetype.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_locale.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_set_calendar.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_set_pattern.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_timetype.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_get_timezone_id.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_is_set_lenient.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_localtime.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_parse.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_parse_localtime_parsepos.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt [new file with mode: 0755]
ext/intl/tests/dateformat_set_timezone_id.phpt [new file with mode: 0755]
ext/intl/tests/formatter_fail.phpt [new file with mode: 0755]
ext/intl/tests/formatter_format.phpt [new file with mode: 0755]
ext/intl/tests/formatter_format_conv.phpt [new file with mode: 0755]
ext/intl/tests/formatter_format_currency.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_error.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_locale.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_set_attribute.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_set_pattern.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_set_symbol.phpt [new file with mode: 0755]
ext/intl/tests/formatter_get_set_text_attribute.phpt [new file with mode: 0755]
ext/intl/tests/formatter_parse.phpt [new file with mode: 0755]
ext/intl/tests/formatter_parse_currency.phpt [new file with mode: 0755]
ext/intl/tests/grapheme.phpt [new file with mode: 0755]
ext/intl/tests/intl_error_name.phpt [new file with mode: 0755]
ext/intl/tests/intl_get_error_code.phpt [new file with mode: 0755]
ext/intl/tests/intl_get_error_message.phpt [new file with mode: 0755]
ext/intl/tests/intl_is_failure.phpt [new file with mode: 0755]
ext/intl/tests/locale_compose_locale.phpt [new file with mode: 0755]
ext/intl/tests/locale_filter_matches.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_all_variants.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_default.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_display_language.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_display_name.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_display_region.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_display_script.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_display_variant.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_keywords.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_primary_language.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_region.phpt [new file with mode: 0755]
ext/intl/tests/locale_get_script.phpt [new file with mode: 0755]
ext/intl/tests/locale_lookup.phpt [new file with mode: 0755]
ext/intl/tests/locale_parse_locale.phpt [new file with mode: 0755]
ext/intl/tests/locale_set_default.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_fail.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_format.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_get_error.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_get_locale.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_get_set_pattern.phpt [new file with mode: 0755]
ext/intl/tests/msgfmt_parse.phpt [new file with mode: 0755]
ext/intl/tests/normalizer_normalize.phpt [new file with mode: 0755]
ext/intl/tests/regression_sort_and_cow.phpt [new file with mode: 0755]
ext/intl/tests/regression_sort_eq.phpt [new file with mode: 0755]
ext/intl/tests/regression_sortwsk_and_cow.phpt [new file with mode: 0755]
ext/intl/tests/regression_sortwsk_eq.phpt [new file with mode: 0755]
ext/intl/tests/ut_common.inc [new file with mode: 0755]

diff --git a/ext/intl/CREDITS b/ext/intl/CREDITS
new file mode 100755 (executable)
index 0000000..3c54042
--- /dev/null
@@ -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 (executable)
index 0000000..786c30a
--- /dev/null
@@ -0,0 +1,3 @@
+- Update dateformat for PHP 6
+- Integrate default locale functionality
+- Integrate collator with standard sorting functions
\ No newline at end of file
diff --git a/ext/intl/collator/collator.c b/ext/intl/collator/collator.c
new file mode 100755 (executable)
index 0000000..4c1bbc7
--- /dev/null
@@ -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 (executable)
index 0000000..96e7aa0
--- /dev/null
@@ -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 (executable)
index 0000000..b4fe0a4
--- /dev/null
@@ -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 (executable)
index 0000000..85636cc
--- /dev/null
@@ -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 (executable)
index 0000000..1d75f71
--- /dev/null
@@ -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 (executable)
index 0000000..8d4c9d9
--- /dev/null
@@ -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 (executable)
index 0000000..34ff4a4
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 )
+{
+       UChar*           str1      = NULL;
+       UChar*           str2      = NULL;
+       int              str1_len  = 0;
+       int              str2_len  = 0;
+
+       UCollationResult result;
+
+       COLLATOR_METHOD_INIT_VARS
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ouu",
+               &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).
+        */
+
+       // Compare given strings.
+       result = ucol_strcoll(
+               co->ucoll,
+               str1, str1_len,
+               str2, str2_len );
+
+       // 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 (executable)
index 0000000..4e38b79
--- /dev/null
@@ -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 (executable)
index 0000000..389f12f
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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>
+
+#define COLLATOR_CONVERT_RETURN_FAILED( retval ) \
+{                                                \
+    zval_add_ref( &retval );                     \
+    return retval;                               \
+}
+
+/* {{{ collator_convert_object_to_string
+ * Convert object to UTF16-encoded string.
+ */
+zval* collator_convert_object_to_string( zval* obj TSRMLS_DC )
+{
+       zval* zstr        = NULL;
+
+       // 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_UNICODE, 0 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 );
+       }
+
+       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_UNICODE )
+       {
+               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_UNICODE )
+       {
+               zend_make_printable_zval(arg, &arg_copy, &use_copy);
+
+               if( use_copy )
+               {
+                       // Don't copy arg_copy data to str.
+                       ALLOC_ZVAL( str );
+                       *str = arg_copy;
+
+                       // Reset refcounter.
+                       INIT_PZVAL( str );
+               }
+               else
+               {
+                       zval_add_ref( &arg );
+                       str = 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_UNICODE )
+       {
+               // 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 );
+
+               zval_add_ref( &arg );
+               n_arg = 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 (executable)
index 0000000..98e224c
--- /dev/null
@@ -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: 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>
+
+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 (executable)
index 0000000..ca0a714
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 = UG(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 = UG(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 (executable)
index 0000000..b740e82
--- /dev/null
@@ -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 (executable)
index 0000000..55e366c
--- /dev/null
@@ -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 (executable)
index 0000000..b2f44ea
--- /dev/null
@@ -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 (executable)
index 0000000..4f84b4d
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ 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 (executable)
index 0000000..585d589
--- /dev/null
@@ -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 (executable)
index 0000000..cfcdf0a
--- /dev/null
@@ -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: 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.
+       RETURN_ASCII_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 (executable)
index 0000000..bda90cd
--- /dev/null
@@ -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 (executable)
index 0000000..b28aef0
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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;
+
+/* {{{ 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_UNICODE && Z_TYPE_P(str2) == IS_UNICODE &&
+               ( 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
+                               zval_add_ref( &num1 );
+                               norm1 = num1;
+
+                               // 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_UNICODE )
+       {
+               num1 = collator_convert_string_to_double( op1 );
+               op1 = num1;
+       }
+
+       if( Z_TYPE_P(op2) == IS_UNICODE )
+       {
+               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 );
+
+       // 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;
+
+       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_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 ) );
+
+       // 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 )
+       {
+               // Process string values only.
+
+               if( Z_TYPE_PP( hashData ) == IS_UNICODE )
+               {
+                       utf16_buf = INTL_Z_STRVAL_P( *hashData );
+                       utf16_len = INTL_Z_STRLEN_P( *hashData );
+               }
+               else
+               {
+                       // Set empty string
+                       utf16_len = 0;
+                       utf16_buf = (UChar*) "";
+               }
+
+               // 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;
+
+       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 (executable)
index 0000000..0fafb9f
--- /dev/null
@@ -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 (executable)
index 0000000..3468cd5
--- /dev/null
@@ -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 (executable)
index 0000000..8716222
--- /dev/null
@@ -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 (executable)
index 0000000..135593c
--- /dev/null
@@ -0,0 +1,58 @@
+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
+  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 \
+               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/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 (executable)
index 0000000..4ae35e5
--- /dev/null
@@ -0,0 +1,65 @@
+// $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 + "/normalizer", "\
+                               normalizer.c \
+                               normalizer_class.c \
+                               normalizer_normalize.c \
+                               ", "intl");
+               ADD_SOURCES(configure_module_dirname + "/grapheme", "\
+                               grapheme_string.c grapheme_util.c  \
+                                ", "intl");
+               ADD_FLAG("LIBS_INTL", "icudt.lib icuin.lib icuio.lib icule.lib iculx.lib");
+               // if int32_t and uint32_t types are made available in PHP, uncomment next line
+               // ADD_FLAG("CFLAGS_INTL", "/D U_HAVE_INT32_T /D U_HAVE_UINT32_T");
+               AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled");
+       } else {
+               WARNING("intl not enabled; libraries and/or headers not found");
+       }
+}
diff --git a/ext/intl/dateformat/dateformat.c b/ext/intl/dateformat/dateformat.c
new file mode 100755 (executable)
index 0000000..b61d268
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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/udat.h>
+#include <unicode/ucal.h>
+
+#include "php_intl.h"
+#include "intl_convert.h"
+#include "dateformat_class.h"
+#include "dateformat.h"
+
+/* {{{ dateformat_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void dateformat_register_constants( INIT_FUNC_ARGS )
+{
+       if( IntlDateFormatter_ce_ptr == NULL) {
+               zend_error(E_ERROR, "DateFormat class not defined");
+               return;
+       }
+
+       #define DATEFORMATTER_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+       #define DATEFORMATTER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( IntlDateFormatter_ce_ptr, ZEND_STRS( #x ) - 1, UDAT_##x TSRMLS_CC );
+       #define DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( IntlDateFormatter_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+       #define DATEFORMATTER_EXPOSE_UCAL_CLASS_CONST(x) zend_declare_class_constant_long( IntlDateFormatter_ce_ptr, ZEND_STRS( #x ) - 1, UCAL_##x TSRMLS_CC );
+
+       // UDateFormatStyle constants
+       DATEFORMATTER_EXPOSE_CLASS_CONST( FULL );
+       DATEFORMATTER_EXPOSE_CLASS_CONST( LONG );
+       DATEFORMATTER_EXPOSE_CLASS_CONST( MEDIUM );
+       DATEFORMATTER_EXPOSE_CLASS_CONST( SHORT );
+       DATEFORMATTER_EXPOSE_CLASS_CONST( NONE );
+
+/*
+       DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "GREGORIAN", DATEF_GREGORIAN );
+       DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "CUSTOMARY", DATEF_CUSTOMARY );
+       DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "BUDDHIST", DATEF_BUDDHIST );
+       DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "JAPANESE_IMPERIAL", DATEF_JAPANESE_IMPERIAL );
+*/
+
+       DATEFORMATTER_EXPOSE_UCAL_CLASS_CONST( GREGORIAN );
+       DATEFORMATTER_EXPOSE_UCAL_CLASS_CONST( TRADITIONAL );
+
+       #undef DATEFORMATTER_EXPOSE_UCAL_CLASS_CONST
+       #undef DATEFORMATTER_EXPOSE_CUSTOM_CLASS_CONST
+       #undef DATEFORMATTER_EXPOSE_CLASS_CONST
+       #undef DATEFORMATTER_EXPOSE_CONST
+}
+/* }}} */
+
+/* {{{ proto IntlDateFormatter IntlDateFormatter::create( string $locale , long date_type, long time_type[,string $timezone_str, long $calendar , string $pattern] )
+ * Create formatter. }}} */
+/* {{{ proto IntlDateFormatter datefmt_create( string $locale, long date_type, long time_type[,string $timezone_str, long $calendar , string $pattern] )
+ * Create formatter.
+ */
+PHP_FUNCTION( datefmt_create )
+{
+       char*       locale;
+       int         locale_len = 0;
+       zval*       object;
+
+        long         date_type = 0;
+        long         time_type = 0;
+        long         calendar = 1;
+        int         all_done = 0;
+        //zval*       timezone = NULL;
+
+        char*       timezone_str = NULL;
+        int         timezone_str_len = 0;
+        char*       pattern_str = NULL;
+        int         pattern_str_len = 0;
+        UChar*      svalue = NULL;             //UTF-16 pattern_str
+        int         slength = 0;
+        UChar*      timezone_utf16 = NULL;             //UTF-16 timezone_str
+        int         timezone_utf16_len = 0;
+       UCalendar   ucal_obj = NULL;
+       
+
+       IntlDateFormatter_object* mfo;
+
+       intl_error_reset( NULL TSRMLS_CC );
+
+       // Parse parameters.
+        if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sll|sls",
+                &locale, &locale_len, &date_type, & time_type , &timezone_str, &timezone_str_len , &calendar ,&pattern_str , &pattern_str_len ) == FAILURE )
+        {
+                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_create: unable to parse input params", 0 TSRMLS_CC );
+                RETURN_NULL();
+        }
+
+
+       // Create a IntlDateFormatter 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, IntlDateFormatter_ce_ptr );
+
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       if(locale_len == 0) {
+               locale = INTL_G(default_locale);
+       }
+
+       // Convert pattern (if specified) to UTF-16.
+       if( pattern_str && pattern_str_len>0 ){
+               intl_convert_utf8_to_utf16(&svalue, &slength, pattern_str, pattern_str_len, &INTL_DATA_ERROR_CODE(mfo));
+               INTL_METHOD_CHECK_STATUS(mfo, "Error converting pattern to UTF-16" );
+       }
+
+       // Convert pattern (if specified) to UTF-16.
+       if( timezone_str && timezone_str_len >0 ){
+               intl_convert_utf8_to_utf16(&timezone_utf16, &timezone_utf16_len, timezone_str, timezone_str_len, &INTL_DATA_ERROR_CODE(mfo));
+               INTL_METHOD_CHECK_STATUS(mfo, "Error converting timezone_str to UTF-16" );
+       }
+
+        // Create an ICU date formatter.
+        while(  U_FAILURE( INTL_DATA_ERROR_CODE(mfo)) || (all_done==0) ){
+               // Convert pattern (if specified) to UTF-16.
+               if( pattern_str && pattern_str_len>0 ){
+                       DATE_FORMAT_OBJECT(mfo) = udat_open(UDAT_IGNORE,UDAT_IGNORE, locale, timezone_utf16, timezone_utf16_len ,svalue ,slength , &INTL_DATA_ERROR_CODE((mfo)));
+               }else{
+                       DATE_FORMAT_OBJECT(mfo) = udat_open(time_type,date_type, locale, timezone_utf16, timezone_utf16_len ,svalue ,slength , &INTL_DATA_ERROR_CODE((mfo)));
+               }
+
+                //Set the calendar if passed
+                if( calendar) {
+                        ucal_obj = ucal_open( timezone_utf16 , timezone_utf16_len , locale , calendar , &INTL_DATA_ERROR_CODE(mfo) );
+                        udat_setCalendar( DATE_FORMAT_OBJECT(mfo), ucal_obj );
+                }
+                all_done = 1;
+
+        }//end of while
+
+
+        if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+        {
+                intl_error_set( NULL, INTL_DATA_ERROR_CODE((mfo)) ,
+                        "__construct: date formatter creation failed", 0 TSRMLS_CC );
+                zval_dtor(object);
+                ZVAL_NULL(object);
+               if( svalue){
+                       efree(svalue);
+               }
+               if( timezone_utf16){
+                       efree(timezone_utf16);
+               }
+                RETURN_NULL();
+        }
+
+       if( svalue){
+               efree(svalue);
+       }
+       if( timezone_utf16){
+               efree(timezone_utf16);
+       }
+       //Set the class variables 
+       mfo->date_type   = date_type;
+       mfo->time_type   = time_type;
+       mfo->calendar   = calendar;
+       if( timezone_str && timezone_str_len > 0){
+               if( mfo->timezone_id ){
+                       efree(mfo->timezone_id);
+               }
+               mfo->timezone_id = estrndup( timezone_str, timezone_str_len);
+       }
+}
+/* }}} */
+
+/* {{{ proto void IntlDateFormatter::__construct( string $locale, string $pattern )
+ * IntlDateFormatter object constructor.
+ */
+PHP_METHOD( IntlDateFormatter, __construct )
+{
+       char*       locale = NULL;
+       int         locale_len = 0;
+       long        date_type = 0;
+       long        time_type = 0;
+       long        calendar = 1;
+
+       char*       timezone_str = NULL;
+       int         timezone_str_len = 0;
+       char*       pattern_str = NULL;
+       int         pattern_str_len = 0;
+       UChar*      svalue = NULL;
+       int         slength = 0;
+        UChar*      timezone_utf16 = NULL;             //UTF-16 timezone_str
+        int         timezone_utf16_len = 0;
+
+       UCalendar   ucal_obj = NULL;
+       int         all_done = 0; 
+
+       zval*       object;
+       IntlDateFormatter_object* mfo;
+
+       intl_error_reset( NULL TSRMLS_CC );
+
+       object = getThis();
+
+       // Parse parameters.
+        if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sll|slsb",
+               &locale, &locale_len, &date_type, & time_type , &timezone_str, &timezone_str_len , &calendar ,&pattern_str , &pattern_str_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();
+       }
+
+/*
+       //Check if timezone is in proper type
+       if( (Z_TYPE_P(timezone) != IS_STRING) && (Z_TYPE_P(timezone) != IS_OBJECT) ){
+               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();
+       }
+*/
+
+       mfo = (IntlDateFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+       intl_error_reset( &mfo->datef_data.error TSRMLS_CC );
+
+       if(locale_len == 0) {
+               locale = INTL_G(default_locale);
+       }
+
+       // Convert pattern (if specified) to UTF-16.
+       if( pattern_str && pattern_str_len>0 ){
+               intl_convert_utf8_to_utf16(&svalue, &slength, pattern_str, pattern_str_len, &INTL_DATA_ERROR_CODE(mfo));
+               INTL_METHOD_CHECK_STATUS(mfo, "Error converting pattern to UTF-16" );
+       }
+
+       // Convert pattern (if specified) to UTF-16.
+       if( timezone_str && timezone_str_len >0 ){
+               intl_convert_utf8_to_utf16(&timezone_utf16, &timezone_utf16_len, timezone_str, timezone_str_len, &INTL_DATA_ERROR_CODE(mfo));
+               INTL_METHOD_CHECK_STATUS(mfo, "Error converting timezone_str to UTF-16" );
+       }
+
+
+        // Create an ICU date formatter.
+        while(  U_FAILURE( INTL_DATA_ERROR_CODE(mfo)) || (all_done==0) ){
+               if( pattern_str && pattern_str_len>0 ){
+                       DATE_FORMAT_OBJECT(mfo) = udat_open(UDAT_IGNORE,UDAT_IGNORE, locale, timezone_utf16, timezone_utf16_len ,svalue ,slength , &INTL_DATA_ERROR_CODE((mfo)));
+               }else{
+                       DATE_FORMAT_OBJECT(mfo) = udat_open(time_type,date_type, locale, timezone_utf16, timezone_utf16_len ,svalue ,slength , &INTL_DATA_ERROR_CODE((mfo)));
+               }
+
+
+                //Set the calendar if passed
+                if( calendar) {
+                        ucal_obj = ucal_open( timezone_utf16 , timezone_utf16_len , locale , calendar , &INTL_DATA_ERROR_CODE(mfo) );
+                        udat_setCalendar( DATE_FORMAT_OBJECT(mfo), ucal_obj );
+                }
+                all_done = 1;
+
+        }//end of while
+
+
+       if( U_FAILURE( INTL_DATA_ERROR_CODE((mfo)) ) )
+       {
+               intl_error_set( NULL, INTL_DATA_ERROR_CODE(mfo),
+                       "__construct: date formatter creation failed", 0 TSRMLS_CC );
+               if( svalue){
+                       efree(svalue);
+               }
+               zval_dtor(object);
+               ZVAL_NULL(object);
+               RETURN_NULL();
+       }
+
+       if( svalue){
+               efree(svalue);
+       }
+
+       //Set the class variables 
+       mfo->date_type   = date_type;
+       mfo->time_type   = time_type;
+       mfo->calendar   = calendar;
+       if( timezone_str && timezone_str_len > 0){
+               mfo->timezone_id = estrndup( timezone_str, timezone_str_len);
+       }
+}
+/* }}} */
+
+/* {{{ proto int IntlDateFormatter::getErrorCode()
+ * Get formatter's last error code. }}} */
+/* {{{ proto int datefmt_get_error_code( IntlDateFormatter $nf )
+ * Get formatter's last error code.
+ */
+PHP_FUNCTION( datefmt_get_error_code )
+{
+       zval*                    object  = NULL;
+       IntlDateFormatter_object*  mfo     = NULL;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+               &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                       "datefmt_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+               RETURN_FALSE;
+       }
+
+       mfo = (IntlDateFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+       // Return formatter's last error code.
+       RETURN_LONG( INTL_DATA_ERROR_CODE(mfo) );
+}
+/* }}} */
+
+/* {{{ proto string IntlDateFormatter::getErrorMessage( )
+ * Get text description for formatter's last error code. }}} */
+/* {{{ proto string datefmt_get_error_message( IntlDateFormatter $coll )
+ * Get text description for formatter's last error code.
+ */
+PHP_FUNCTION( datefmt_get_error_message )
+{
+       char*                    message = NULL;
+       zval*                    object  = NULL;
+       IntlDateFormatter_object*  mfo     = NULL;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+               &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                       "datefmt_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+               RETURN_FALSE;
+       }
+
+       mfo = (IntlDateFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+       // Return last error message.
+       message = intl_error_get_message( &mfo->datef_data.error TSRMLS_CC );
+       RETURN_STRING( message, 0);
+}
+/* }}} */
diff --git a/ext/intl/dateformat/dateformat.h b/ext/intl/dateformat/dateformat.h
new file mode 100755 (executable)
index 0000000..f11918b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 DATE_FORMATTER_H
+#define DATE_FORMATTER_H
+
+#include <php.h>
+
+PHP_FUNCTION( datefmt_create );
+PHP_FUNCTION( datefmt_get_error_code );
+PHP_FUNCTION( datefmt_get_error_message );
+PHP_METHOD( IntlDateFormatter, __construct );
+void dateformat_register_constants( INIT_FUNC_ARGS );
+
+/*
+These are not necessary at this point of time
+#define DATEF_GREGORIAN 1
+#define DATEF_CUSTOMARY 2
+#define DATEF_BUDDHIST 3
+#define DATEF_JAPANESE_IMPERIAL 4
+*/
+
+#define CALENDAR_SEC "tm_sec"
+#define CALENDAR_MIN "tm_min"
+#define CALENDAR_HOUR "tm_hour"
+#define CALENDAR_MDAY "tm_mday"
+#define CALENDAR_MON "tm_mon"
+#define CALENDAR_YEAR "tm_year"
+#define CALENDAR_WDAY "tm_wday"
+#define CALENDAR_YDAY "tm_yday"
+#define CALENDAR_ISDST "tm_isdst"
+
+#endif // DATE_FORMATTER_H
diff --git a/ext/intl/dateformat/dateformat_attr.c b/ext/intl/dateformat/dateformat_attr.c
new file mode 100755 (executable)
index 0000000..09997b6
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 "php_intl.h"
+#include "intl_convert.h"
+#include "dateformat_class.h"
+#include "dateformat_attr.h"
+
+#include <unicode/ustring.h>
+#include <unicode/udat.h>
+#include <unicode/ucal.h>
+
+static void internal_set_calendar(IntlDateFormatter_object *mfo, char* timezone_id , int timezone_id_len , int calendar  ,zval* return_value TSRMLS_DC){
+
+        int         timezone_utf16_len = 0;
+        UChar*      timezone_utf16  = NULL;             //timezone_id in UTF-16
+
+        char*       locale = NULL;
+        int         locale_type =ULOC_ACTUAL_LOCALE;
+
+        UCalendar*   ucal_obj = NULL;
+
+        //check for the validity  of value of calendar passed
+        intl_error_reset( NULL TSRMLS_CC );
+        if( calendar > 1){
+                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_set_calendar: calendar value specified is out of valid range", 0 TSRMLS_CC);
+                RETURN_FALSE;
+        }
+
+        // Convert timezone to UTF-16.
+        intl_convert_utf8_to_utf16(&timezone_utf16, &timezone_utf16_len, timezone_id, timezone_id_len  , &INTL_DATA_ERROR_CODE(mfo));
+        INTL_METHOD_CHECK_STATUS(mfo, "Error converting timezone to UTF-16" );
+
+
+        //Get the lcoale for the dateformatter
+        locale = (char *)udat_getLocaleByType(DATE_FORMAT_OBJECT(mfo), locale_type ,&INTL_DATA_ERROR_CODE(mfo));
+
+        //Set the calendar if passed
+        ucal_obj = ucal_open( timezone_utf16 , timezone_utf16_len , locale , calendar , &INTL_DATA_ERROR_CODE(mfo) );
+        udat_setCalendar( DATE_FORMAT_OBJECT(mfo), ucal_obj );
+        INTL_METHOD_CHECK_STATUS(mfo, "Error setting the calendar.");
+
+        if( timezone_utf16){
+                efree(timezone_utf16);
+        }
+
+}
+
+/* {{{ proto unicode IntlDateFormatter::getDateType( )
+ * Get formatter datetype. }}} */
+/* {{{ proto string datefmt_get_datetype( IntlDateFormatter $mf )
+ * Get formatter datetype.
+ */
+PHP_FUNCTION( datefmt_get_datetype )
+{
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL , U_ILLEGAL_ARGUMENT_ERROR,        
+                       "datefmt_get_datetype: unable to parse input params", 0 TSRMLS_CC );
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       INTL_METHOD_CHECK_STATUS(mfo, "Error getting formatter datetype." );
+
+       RETURN_LONG(mfo->date_type );
+}
+/* }}} */
+
+/* {{{ proto unicode IntlDateFormatter::getTimeType( )
+ * Get formatter timetype. }}} */
+/* {{{ proto string datefmt_get_timetype( IntlDateFormatter $mf )
+ * Get formatter timetype.
+ */
+PHP_FUNCTION( datefmt_get_timetype )
+{
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL , U_ILLEGAL_ARGUMENT_ERROR,        
+                       "datefmt_get_timetype: unable to parse input params", 0 TSRMLS_CC );
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       INTL_METHOD_CHECK_STATUS(mfo, "Error getting formatter timetype." );
+
+       RETURN_LONG(mfo->time_type );
+}
+/* }}} */
+
+
+/* {{{ proto unicode IntlDateFormatter::getCalendar( )
+ * Get formatter calendar. }}} */
+/* {{{ proto string datefmt_get_calendar( IntlDateFormatter $mf )
+ * Get formatter calendar.
+ */
+PHP_FUNCTION( datefmt_get_calendar )
+{
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL , U_ILLEGAL_ARGUMENT_ERROR,        
+                       "datefmt_get_calendar: unable to parse input params", 0 TSRMLS_CC );
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       INTL_METHOD_CHECK_STATUS(mfo, "Error getting formatter calendar." );
+
+       RETURN_LONG(mfo->calendar );
+}
+/* }}} */
+
+/* {{{ proto unicode IntlDateFormatter::getTimeZoneId( )
+ * Get formatter timezone_id. }}} */
+/* {{{ proto string datefmt_get_timezone_id( IntlDateFormatter $mf )
+ * Get formatter timezone_id.
+ */
+PHP_FUNCTION( datefmt_get_timezone_id )
+{
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL , U_ILLEGAL_ARGUMENT_ERROR,        
+                       "datefmt_get_timezone_id: unable to parse input params", 0 TSRMLS_CC );
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       INTL_METHOD_CHECK_STATUS(mfo, "Error getting formatter timezone_id." );
+
+       if( mfo->timezone_id ){
+               RETURN_STRING((char*)mfo->timezone_id ,TRUE );
+       }else{
+               RETURN_NULL();
+       }
+}
+
+/* {{{ proto boolean IntlDateFormatter::setTimeZoneId( $timezone_id)
+ * Set formatter timezone_id. }}} */
+/* {{{ proto boolean datefmt_set_timezone_id( IntlDateFormatter $mf ,$timezone_id)
+ * Set formatter timezone_id.
+ */
+PHP_FUNCTION( datefmt_set_timezone_id )
+{
+       char*           timezone_id             = NULL;
+       int             timezone_id_len         = 0;
+
+        DATE_FORMAT_METHOD_INIT_VARS;
+
+        // Parse parameters.
+        if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, IntlDateFormatter_ce_ptr ,&timezone_id , &timezone_id_len) == FAILURE )
+        {
+                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_set_timezone_id: unable to parse input params", 0 TSRMLS_CC );
+                RETURN_FALSE;
+        }
+
+        // Fetch the object.
+        DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       //set the timezone for the calendar
+       internal_set_calendar( mfo , timezone_id , timezone_id_len , mfo->calendar ,return_value TSRMLS_CC );
+
+       //Set the IntlDateFormatter variable
+        if( mfo->timezone_id ){
+               efree(mfo->timezone_id);
+       }
+       mfo->timezone_id = estrndup(timezone_id , timezone_id_len);
+
+       RETURN_TRUE;
+}
+
+/* {{{ proto string IntlDateFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string datefmt_get_pattern( IntlDateFormatter $mf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( datefmt_get_pattern )
+{
+       UChar  value_buf[64];
+       int    length = USIZE( value_buf );
+       UChar* value  = value_buf;
+       zend_bool   is_pattern_localized =FALSE;
+
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, 
+                       "datefmt_get_pattern: unable to parse input params", 0 TSRMLS_CC );
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       length = udat_toPattern(DATE_FORMAT_OBJECT(mfo), is_pattern_localized, value, length, &INTL_DATA_ERROR_CODE(mfo));
+       if(INTL_DATA_ERROR_CODE(mfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
+               ++length; // to avoid U_STRING_NOT_TERMINATED_WARNING
+               INTL_DATA_ERROR_CODE(mfo) = U_ZERO_ERROR;
+               value = eumalloc(length);
+               length = udat_toPattern(DATE_FORMAT_OBJECT(mfo), is_pattern_localized , value, length, &INTL_DATA_ERROR_CODE(mfo) );
+               if(U_FAILURE(INTL_DATA_ERROR_CODE(mfo))) {
+                       efree(value);
+                       value = value_buf;
+               }
+       }
+       INTL_METHOD_CHECK_STATUS(mfo, "Error getting formatter pattern" );
+
+       INTL_METHOD_RETVAL_UTF8( mfo, value, length, ( value != value_buf ) );
+}
+/* }}} */
+
+/* {{{ proto bool IntlDateFormatter::setPattern( string $pattern )
+ * Set formatter pattern. }}} */
+/* {{{ proto bool datefmt_set_pattern( IntlDateFormatter $mf, string $pattern )
+ * Set formatter pattern.
+ */
+PHP_FUNCTION( datefmt_set_pattern )
+{
+       char*       value = NULL;
+       int         value_len = 0;
+       int         slength = 0;
+       UChar*      svalue  = NULL;
+       zend_bool   is_pattern_localized =FALSE;
+
+
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+               &object, IntlDateFormatter_ce_ptr,  &value, &value_len ) == FAILURE )
+       {
+               intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,  
+                       "datefmt_set_pattern: unable to parse input params", 0 TSRMLS_CC);
+               RETURN_FALSE;
+       }
+
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       // Convert given pattern to UTF-16.
+       intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(mfo));
+       INTL_METHOD_CHECK_STATUS(mfo, "Error converting pattern to UTF-16" );
+
+       udat_applyPattern(DATE_FORMAT_OBJECT(mfo), (UBool)is_pattern_localized , svalue, slength);
+
+       efree(svalue);
+       INTL_METHOD_CHECK_STATUS(mfo, "Error setting symbol value");
+
+       RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string IntlDateFormatter::getLocale()
+ * Get formatter locale. }}} */
+/* {{{ proto string datefmt_get_locale(IntlDateFormatter $mf)
+ * Get formatter locale.
+ */
+PHP_FUNCTION( datefmt_get_locale )
+{
+       char *loc;
+       long  loc_type =ULOC_ACTUAL_LOCALE;
+
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l",
+               &object, IntlDateFormatter_ce_ptr ,&loc_type) == FAILURE )
+       {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                       "datefmt_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       loc = (char *)udat_getLocaleByType(DATE_FORMAT_OBJECT(mfo), loc_type ,&INTL_DATA_ERROR_CODE(mfo));
+       RETURN_STRING(loc, 1);
+}
+/* }}} */
+
+/* {{{ proto string IntlDateFormatter::isLenient()
+ * Get formatter isLenient. }}} */
+/* {{{ proto string datefmt_isLenient(IntlDateFormatter $mf)
+ * Get formatter locale.
+ */
+PHP_FUNCTION( datefmt_is_lenient )
+{
+       
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+               &object, IntlDateFormatter_ce_ptr ) == FAILURE )
+       {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                       "datefmt_is_lenient: unable to parse input params", 0 TSRMLS_CC );
+
+               RETURN_FALSE;
+       }
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       RETVAL_BOOL(udat_isLenient(DATE_FORMAT_OBJECT(mfo)));
+}
+/* }}} */
+
+/* {{{ proto string IntlDateFormatter::setLenient()
+ * Set formatter lenient. }}} */
+/* {{{ proto string datefmt_setLenient(IntlDateFormatter $mf)
+ * Set formatter lenient.
+ */
+PHP_FUNCTION( datefmt_set_lenient )
+{
+
+        zend_bool isLenient  = FALSE;
+
+        DATE_FORMAT_METHOD_INIT_VARS;
+
+        // Parse parameters.
+        if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ob",
+                &object, IntlDateFormatter_ce_ptr ,&isLenient ) == FAILURE )
+        {
+                intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_set_lenient: unable to parse input params", 0 TSRMLS_CC );
+
+                RETURN_FALSE;
+        }
+
+        // Fetch the object.
+        DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+        udat_setLenient(DATE_FORMAT_OBJECT(mfo) , (UBool)isLenient );
+}
+/* }}} */
+
+/* {{{ proto bool IntlDateFormatter::setPattern( int $calendar )
+ * Set formatter calendar. }}} */
+/* {{{ proto bool datefmt_set_calendar( IntlDateFormatter $mf, int $calendar )
+ * Set formatter calendar.
+ */
+PHP_FUNCTION( datefmt_set_calendar )
+{
+       long    calendar = 0;
+
+        DATE_FORMAT_METHOD_INIT_VARS;
+
+        // Parse parameters.
+        if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+                &object, IntlDateFormatter_ce_ptr, &calendar ) == FAILURE )
+        {
+                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_set_calendar: unable to parse input params", 0 TSRMLS_CC);
+                RETURN_FALSE;
+        }
+
+       //check for the validity  of value of calendar passed
+       intl_error_reset( NULL TSRMLS_CC );
+       if( calendar > 1){
+                intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_set_calendar: calendar value specified is out of valid range", 0 TSRMLS_CC);
+                RETURN_FALSE;
+       }
+
+        DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       internal_set_calendar( mfo , mfo->timezone_id , strlen(mfo->timezone_id) , calendar ,return_value TSRMLS_CC );
+
+       //Set the calendar  value in the IntlDateFormatter object
+        mfo->calendar = calendar ;
+
+        RETURN_TRUE;
+}
+/* }}} */
+
+
diff --git a/ext/intl/dateformat/dateformat_attr.h b/ext/intl/dateformat/dateformat_attr.h
new file mode 100755 (executable)
index 0000000..bf28824
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 DATE_FORMAT_ATTR_H
+#define DATE_FORMAT_ATTR_H
+
+#include <php.h>
+
+//PHP_FUNCTION( datefmt_get_timezone );
+PHP_FUNCTION( datefmt_get_datetype );
+PHP_FUNCTION( datefmt_get_timetype );
+PHP_FUNCTION( datefmt_get_calendar );
+PHP_FUNCTION( datefmt_set_calendar );
+PHP_FUNCTION( datefmt_get_locale );
+PHP_FUNCTION( datefmt_get_timezone_id );
+PHP_FUNCTION( datefmt_set_timezone_id );
+PHP_FUNCTION( datefmt_get_pattern );
+PHP_FUNCTION( datefmt_set_pattern );
+PHP_FUNCTION( datefmt_is_lenient );
+PHP_FUNCTION( datefmt_set_lenient );
+
+#endif // DATE_FORMAT_ATTR_H
diff --git a/ext/intl/dateformat/dateformat_class.c b/ext/intl/dateformat/dateformat_class.c
new file mode 100755 (executable)
index 0000000..e582467
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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/unum.h>
+
+#include "dateformat_class.h"
+#include "php_intl.h"
+#include "dateformat_data.h"
+#include "dateformat_format.h"
+#include "dateformat_parse.h"
+#include "dateformat.h"
+#include "dateformat_attr.h"
+
+zend_class_entry *IntlDateFormatter_ce_ptr = NULL;
+
+/////////////////////////////////////////////////////////////////////////////
+// Auxiliary functions needed by objects of 'IntlDateFormatter' class
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ IntlDateFormatter_objects_dtor */
+static void IntlDateFormatter_object_dtor(void *object, zend_object_handle handle TSRMLS_DC )
+{
+       zend_objects_destroy_object( object, handle TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ IntlDateFormatter_objects_free */
+void IntlDateFormatter_object_free( zend_object *object TSRMLS_DC )
+{
+       IntlDateFormatter_object* mfo = (IntlDateFormatter_object*)object;
+
+       zend_object_std_dtor( &mfo->zo TSRMLS_CC );
+
+       dateformat_data_free( &mfo->datef_data TSRMLS_CC );
+       
+       if( mfo->timezone_id ){
+               efree(mfo->timezone_id);
+       }
+
+       efree( mfo );
+}
+/* }}} */
+
+/* {{{ IntlDateFormatter_object_create */
+zend_object_value IntlDateFormatter_object_create(zend_class_entry *ce TSRMLS_DC)
+{
+       zend_object_value    retval;
+       IntlDateFormatter_object*     intern;
+
+       intern = ecalloc( 1, sizeof(IntlDateFormatter_object) );
+       dateformat_data_init( &intern->datef_data TSRMLS_CC );
+       zend_object_std_init( &intern->zo, ce TSRMLS_CC );
+       intern->date_type = 0;
+       intern->time_type = 0;
+       intern->calendar  = 1;          //Gregorian calendar
+       intern->timezone_id =  NULL;
+
+       retval.handle = zend_objects_store_put(
+               intern,
+               IntlDateFormatter_object_dtor,
+               (zend_objects_free_object_storage_t)IntlDateFormatter_object_free,
+               NULL TSRMLS_CC );
+
+       retval.handlers = zend_get_std_object_handlers();
+
+       return retval;
+}
+/* }}} */
+
+/////////////////////////////////////////////////////////////////////////////
+// 'IntlDateFormatter' class registration structures & functions
+/////////////////////////////////////////////////////////////////////////////
+
+/* {{{ IntlDateFormatter_class_functions
+ * Every 'IntlDateFormatter' class method has an entry in this table
+ */
+
+static function_entry IntlDateFormatter_class_functions[] = {
+       PHP_ME( IntlDateFormatter, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+       ZEND_FENTRY(  create, ZEND_FN( datefmt_create ), NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+       PHP_NAMED_FE( getDateType, ZEND_FN( datefmt_get_datetype ), NULL )
+       PHP_NAMED_FE( getTimeType, ZEND_FN( datefmt_get_timetype ), NULL )
+       PHP_NAMED_FE( getCalendar, ZEND_FN( datefmt_get_calendar ), NULL )
+       PHP_NAMED_FE( setCalendar, ZEND_FN( datefmt_set_calendar ), NULL )
+       PHP_NAMED_FE( getTimeZoneId, ZEND_FN( datefmt_get_timezone_id ), NULL )
+       PHP_NAMED_FE( setTimeZoneId, ZEND_FN( datefmt_set_timezone_id ), NULL )
+       PHP_NAMED_FE( setPattern, ZEND_FN( datefmt_set_pattern ), NULL )
+       PHP_NAMED_FE( getPattern, ZEND_FN( datefmt_get_pattern ), NULL )
+       PHP_NAMED_FE( getLocale, ZEND_FN( datefmt_get_locale ), NULL )
+       PHP_NAMED_FE( setLenient, ZEND_FN( datefmt_set_lenient ), NULL )
+       PHP_NAMED_FE( isLenient, ZEND_FN( datefmt_is_lenient ), NULL )
+       PHP_NAMED_FE( format, ZEND_FN( datefmt_format ), NULL )
+       PHP_NAMED_FE( parse, ZEND_FN( datefmt_parse), NULL )
+       PHP_NAMED_FE( localtime, ZEND_FN( datefmt_localtime ), NULL )
+       PHP_NAMED_FE( getErrorCode, ZEND_FN( datefmt_get_error_code ), NULL )
+       PHP_NAMED_FE( getErrorMessage, ZEND_FN( datefmt_get_error_message ), NULL )
+       { NULL, NULL, NULL }
+};
+/* }}} */
+
+/* {{{ dateformat_register_class
+ * Initialize 'IntlDateFormatter' class
+ */
+void dateformat_register_IntlDateFormatter_class( TSRMLS_D )
+{
+       zend_class_entry ce;
+
+       // Create and register 'IntlDateFormatter' class.
+       INIT_CLASS_ENTRY( ce, "IntlDateFormatter", IntlDateFormatter_class_functions );
+       ce.create_object = IntlDateFormatter_object_create;
+       IntlDateFormatter_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+       // Declare 'IntlDateFormatter' class properties.
+       if( !IntlDateFormatter_ce_ptr )
+       {
+               zend_error(E_ERROR, "Failed to register IntlDateFormatter class");
+               return;
+       }
+}
+/* }}} */
diff --git a/ext/intl/dateformat/dateformat_class.h b/ext/intl/dateformat/dateformat_class.h
new file mode 100755 (executable)
index 0000000..039ff1a
--- /dev/null
@@ -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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
+   +----------------------------------------------------------------------+
+*/
+#ifndef DATE_FORMAT_CLASS_H
+#define DATE_FORMAT_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+#include "intl_data.h"
+#include "dateformat_data.h"
+
+typedef struct {
+       zend_object             zo;
+       dateformat_data         datef_data;
+       int                     date_type ;
+       int                     time_type ;
+       int                     calendar ;
+       char*                   timezone_id;
+} IntlDateFormatter_object;
+
+void dateformat_register_IntlDateFormatter_class( TSRMLS_D );
+extern zend_class_entry *IntlDateFormatter_ce_ptr;
+
+/* Auxiliary macros */
+
+#define DATE_FORMAT_METHOD_INIT_VARS   INTL_METHOD_INIT_VARS(IntlDateFormatter, mfo)
+#define DATE_FORMAT_METHOD_FETCH_OBJECT        INTL_METHOD_FETCH_OBJECT(IntlDateFormatter, mfo)
+#define DATE_FORMAT_OBJECT(mfo)                (mfo)->datef_data.udatf
+
+#endif // #ifndef DATE_FORMAT_CLASS_H
diff --git a/ext/intl/dateformat/dateformat_data.c b/ext/intl/dateformat/dateformat_data.c
new file mode 100755 (executable)
index 0000000..33451e1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 "dateformat_data.h"
+
+/* {{{ void dateformat_data_init( dateformat_data* datef_data )
+ * Initialize internals of dateformat_data.
+ */
+void dateformat_data_init( dateformat_data* datef_data TSRMLS_DC )
+{
+       if( !datef_data )
+               return;
+
+       datef_data->udatf = NULL;
+       intl_error_reset( &datef_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void dateformat_data_free( dateformat_data* datef_data )
+ * Clean up memory allocated for dateformat_data
+ */
+void dateformat_data_free( dateformat_data* datef_data TSRMLS_DC )
+{
+       if( !datef_data )
+               return;
+
+       if( datef_data->udatf )
+               udat_close( datef_data->udatf );
+
+       datef_data->udatf = NULL;
+       intl_error_reset( &datef_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ dateformat_data* dateformat_data_create()
+ * Allocate memory for dateformat_data and initialize it with default values.
+ */
+dateformat_data* dateformat_data_create( TSRMLS_D )
+{
+       dateformat_data* datef_data = ecalloc( 1, sizeof(dateformat_data) );
+
+       dateformat_data_init( datef_data TSRMLS_CC );
+
+       return datef_data;
+}
+/* }}} */
diff --git a/ext/intl/dateformat/dateformat_data.h b/ext/intl/dateformat/dateformat_data.h
new file mode 100755 (executable)
index 0000000..cde9e36
--- /dev/null
@@ -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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
+   +----------------------------------------------------------------------+
+*/
+#ifndef DATE_FORMAT_DATA_H
+#define DATE_FORMAT_DATA_H
+
+#include <php.h>
+
+#include <unicode/udat.h>
+
+#include "intl_error.h"
+
+typedef struct {
+       // error hangling
+       intl_error      error;
+
+       // formatter handling
+       UDateFormat *  udatf;
+} dateformat_data;
+
+dateformat_data* dateformat_data_create( TSRMLS_D );
+void dateformat_data_init( dateformat_data* datef_data TSRMLS_DC );
+void dateformat_data_free( dateformat_data* datef_data TSRMLS_DC );
+
+#endif // DATE_FORMAT_DATA_H
diff --git a/ext/intl/dateformat/dateformat_format.c b/ext/intl/dateformat/dateformat_format.c
new file mode 100755 (executable)
index 0000000..c23ab32
--- /dev/null
@@ -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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
+   +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include <unicode/ucal.h>
+
+#include "php_intl.h"
+#include "intl_convert.h"
+#include "dateformat.h"
+#include "dateformat_class.h"
+#include "dateformat_format.h"
+#include "dateformat_data.h"
+
+/* {{{ 
+ * Internal function which calls the udat_format
+*/
+static void internal_format(IntlDateFormatter_object *mfo, UDate timestamp , zval *return_value TSRMLS_DC){
+       UChar*  formatted =  NULL;
+       int32_t resultlengthneeded =0 ;
+       
+       resultlengthneeded=udat_format( DATE_FORMAT_OBJECT(mfo), timestamp, NULL, resultlengthneeded, NULL, &INTL_DATA_ERROR_CODE(mfo));
+       if(INTL_DATA_ERROR_CODE(mfo)==U_BUFFER_OVERFLOW_ERROR)
+       {
+               INTL_DATA_ERROR_CODE(mfo)=U_ZERO_ERROR;
+               formatted=(UChar*)emalloc(sizeof(UChar) * resultlengthneeded); 
+               udat_format( DATE_FORMAT_OBJECT(mfo), timestamp, formatted, resultlengthneeded, NULL, &INTL_DATA_ERROR_CODE(mfo));
+       }
+
+       if (formatted && U_FAILURE( INTL_DATA_ERROR_CODE(mfo) ) ) {
+                       efree(formatted);
+       }
+
+       INTL_METHOD_CHECK_STATUS( mfo, "Date formatting failed" );
+       INTL_METHOD_RETVAL_UTF8( mfo, formatted, resultlengthneeded, 1 );
+
+}
+/* }}} */
+
+
+/* {{{ 
+ * Internal function which fetches an element from the passed array for the key_name passed 
+*/
+static double internal_get_arr_ele(IntlDateFormatter_object *mfo  , HashTable* hash_arr  ,char* key_name TSRMLS_DC){
+       zval**  ele_value       = NULL;
+       UDate result = -1;
+
+        if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ){
+                if( Z_TYPE_PP(ele_value)!= IS_LONG ){
+                       intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                               "datefmt_format: parameter array does not contain a long element.", 0 TSRMLS_CC );
+                }else{
+                       result =  Z_LVAL_PP(ele_value);
+               }
+       }
+       //printf("\n Inside internal_get_arr_ele key_name= %s , result = %g \n" , key_name, result);
+       return result;
+}
+/* }}} */
+
+/* {{{ 
+ * Internal function which creates a UCalendar  from the passed array
+*/
+static void internal_create_ucal(IntlDateFormatter_object *mfo, HashTable* hash_arr , UCalendar* pcal  TSRMLS_DC){
+       long year =0;
+       long month =0;
+       long hour =0;
+       long minute =0;
+       long second =0;
+       long wday =0;
+       long yday =0;
+       long mday =0;
+       UBool isInDST = FALSE;
+
+       //Fetch  values from the incoming array
+       year = internal_get_arr_ele( mfo , hash_arr , CALENDAR_YEAR TSRMLS_CC) + 1900; //tm_year is years since 1900
+       //Month in ICU and PHP starts from January =0
+       month = internal_get_arr_ele( mfo , hash_arr , CALENDAR_MON TSRMLS_CC);
+       hour = internal_get_arr_ele( mfo , hash_arr , CALENDAR_HOUR TSRMLS_CC);
+       minute = internal_get_arr_ele( mfo , hash_arr , CALENDAR_MIN TSRMLS_CC);
+       second = internal_get_arr_ele( mfo , hash_arr , CALENDAR_SEC TSRMLS_CC);
+       wday = internal_get_arr_ele( mfo , hash_arr , CALENDAR_WDAY TSRMLS_CC);
+       yday = internal_get_arr_ele( mfo , hash_arr , CALENDAR_YDAY TSRMLS_CC);
+       isInDST = internal_get_arr_ele( mfo , hash_arr , CALENDAR_ISDST TSRMLS_CC);
+       //For the ucal_setDateTime() function , this is the 'date'  value
+       mday = internal_get_arr_ele( mfo , hash_arr , CALENDAR_MDAY TSRMLS_CC);
+
+       //set the incoming values for the calendar      
+       ucal_setDateTime( pcal, year, month  , mday , hour , minute , second , &INTL_DATA_ERROR_CODE(mfo));
+       if( INTL_DATA_ERROR_CODE(mfo) != U_ZERO_ERROR){
+               return;
+       }
+       //ICU UCAL_DAY_OF_WEEK starts from SUNDAY=1  thru SATURDAY=7 
+       //whereas PHP localtime has tm_wday SUNDAY=0 thru SATURDAY=6
+       ucal_set( pcal, UCAL_DAY_OF_WEEK , (wday+1));
+       ucal_set( pcal, UCAL_DAY_OF_YEAR , yday);
+       
+       //TO DO :How to set the isInDST field?Is it required to set
+}
+
+
+/* {{{ proto string IntlDateFormatter::format( [mixed]int $args or array $args )
+ * Format the time value as a string. }}}*/
+/* {{{ proto string datefmt_format( [mixed]int $args or array $args )
+ * Format the time value as a string. }}}*/
+PHP_FUNCTION(datefmt_format) 
+{
+       UDate           timestamp =0;
+       UDate           p_timestamp =0;
+       UCalendar*      temp_cal ; 
+       HashTable*      hash_arr        = NULL;
+       zval*           zarg    = NULL;
+
+       DATE_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz", &object, IntlDateFormatter_ce_ptr ,&zarg ) == FAILURE )
+        {
+               intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                        "datefmt_format: unable to parse input params", 0 TSRMLS_CC );
+                RETURN_FALSE;
+        }
+
+
+       // Fetch the object.
+       DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+
+       switch(Z_TYPE_P(zarg) ){
+               case IS_LONG:
+                       p_timestamp = Z_LVAL_P(zarg) ;
+                       timestamp = p_timestamp * 1000;
+                       break;
+               case IS_DOUBLE:
+                       //timestamp*1000 since ICU expects it in milliseconds
+                       p_timestamp = Z_DVAL_P(zarg) ;
+                       timestamp = p_timestamp * 1000;
+                       break;
+               case IS_ARRAY:
+                       hash_arr = Z_ARRVAL_P(zarg);
+                       if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
+                               RETURN_FALSE;
+                       //Create a UCalendar object from the array and then format it
+                       temp_cal = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &INTL_DATA_ERROR_CODE(mfo));
+                       ucal_clear(temp_cal);
+                       INTL_METHOD_CHECK_STATUS( mfo, "datefmt_format: Date formatting failed while creating calendar from the  array" )
+                       internal_create_ucal( mfo ,  hash_arr , temp_cal TSRMLS_CC);
+                       INTL_METHOD_CHECK_STATUS( mfo, "datefmt_format: Date formatting failed while creating calendar from the  array" )
+                       //Fetch the timestamp from the  created UCalendar
+                       timestamp  = ucal_getMillis(temp_cal  , &INTL_DATA_ERROR_CODE(mfo) );
+                       INTL_METHOD_CHECK_STATUS( mfo, "datefmt_format: Date formatting failed" )
+                       break;
+/*
+               case IS_OBJECT:
+                       break;
+*/
+               default:
+                       intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                               "datefmt_format: takes either an array  or an integer TimeStamp value ", 0 TSRMLS_CC );
+                       RETURN_FALSE;
+       }
+
+       internal_format( mfo, timestamp ,return_value TSRMLS_CC);
+       
+}
+
+/* }}} */
+
diff --git a/ext/intl/dateformat/dateformat_format.h b/ext/intl/dateformat/dateformat_format.h
new file mode 100755 (executable)
index 0000000..49f34c6
--- /dev/null
@@ -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 DATE_FORMAT_FORMAT_H
+#define DATE_FORMAT_FORMAT_H
+
+#include <php.h>
+
+PHP_FUNCTION( datefmt_format );
+
+#endif // DATE_FORMAT_FORMAT_H
diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c
new file mode 100755 (executable)
index 0000000..a27171b
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 "php_intl.h"
+#include "intl_convert.h"
+#include "dateformat.h"
+#include "dateformat_class.h"
+#include "dateformat_parse.h"
+#include "dateformat_data.h"
+
+/* {{{ 
+ * Internal function which calls the udat_parse
+ * param int store_error acts like a boolean 
+ *     if set to 1 - store any error encountered  in the parameter parse_error  
+ *     if set to 0 - no need to store any error encountered  in the parameter parse_error  
+*/
+static void internal_parse_to_timestamp(IntlDateFormatter_object *mfo, char* text_to_parse , int32_t text_len, int parse_pos , zval *return_value TSRMLS_DC){
+       long    result =  0;
+       UDate   timestamp   =0;
+       UChar*  text_utf16  = NULL;
+       int32_t text_utf16_len = 0;
+
+       // Convert timezone to UTF-16.
+        intl_convert_utf8_to_utf16(&text_utf16 , &text_utf16_len , text_to_parse , text_len, &INTL_DATA_ERROR_CODE(mfo));
+        INTL_METHOD_CHECK_STATUS(mfo, "Error converting timezone to UTF-16" );
+
+       timestamp = udat_parse( DATE_FORMAT_OBJECT(mfo), text_utf16 , text_utf16_len , &parse_pos , &INTL_DATA_ERROR_CODE(mfo));
+       if( text_utf16 ){
+               efree(text_utf16);
+       }
+
+       INTL_METHOD_CHECK_STATUS( mfo, "Date parsing failed" );
+       
+       //Since return is in  sec.
+       result = (long )( timestamp / 1000 );
+       if( result != (timestamp/1000) ) {
+               intl_error_set( NULL, U_BUFFER_OVERFLOW_ERROR,
+                                "datefmt_parse: parsing of input parametrs resulted in value larger than data type long can handle.\nThe 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.", 0 TSRMLS_CC );
+       }
+       RETURN_LONG( result );
+}
+/* }}} */
+
+static void add_to_localtime_arr( IntlDateFormatter_object *mfo, zval* return_value ,UCalendar parsed_calendar , long calendar_field , char* key_name TSRMLS_DC){
+       long calendar_field_val = ucal_get( parsed_calendar , calendar_field , &INTL_DATA_ERROR_CODE(mfo));     
+       INTL_METHOD_CHECK_STATUS( mfo, "Date parsing - localtime failed : could not get a field from calendar" );
+
+       if( strcmp(key_name , CALENDAR_YEAR )==0 ){
+               //since tm_year is years from 1900
+               add_assoc_long( return_value, key_name ,( calendar_field_val-1900) ); 
+       }else if( strcmp(key_name , CALENDAR_WDAY )==0 ){
+               //since tm_wday starts from 0 whereas ICU WDAY start from 1
+               add_assoc_long( return_value, key_name ,( calendar_field_val-1) ); 
+       }else{
+               add_assoc_long( return_value, key_name , calendar_field_val ); 
+       }
+}
+
+/* {{{
+ * Internal function which calls the udat_parseCalendar
+*/
+static void internal_parse_to_localtime(IntlDateFormatter_object *mfo, char* text_to_parse , int32_t text_len, int parse_pos , zval *return_value TSRMLS_DC){
+        UCalendar*     parsed_calendar = NULL ;
+        UChar*         text_utf16  = NULL;
+        int32_t        text_utf16_len = 0;
+       long            isInDST = 0;
+
+        // Convert timezone to UTF-16.
+        intl_convert_utf8_to_utf16(&text_utf16 , &text_utf16_len , text_to_parse , text_len, &INTL_DATA_ERROR_CODE(mfo));
+        INTL_METHOD_CHECK_STATUS(mfo, "Error converting timezone to UTF-16" );
+
+       parsed_calendar = ucal_open(NULL, -1, NULL, UCAL_GREGORIAN, &INTL_DATA_ERROR_CODE(mfo));
+        udat_parseCalendar( DATE_FORMAT_OBJECT(mfo), parsed_calendar , text_utf16 , text_utf16_len , &parse_pos , &INTL_DATA_ERROR_CODE(mfo));
+        if( text_utf16 ){
+                efree(text_utf16);
+        }
+
+        INTL_METHOD_CHECK_STATUS( mfo, "Date parsing failed" );
+
+
+       array_init( return_value );
+       //Add  entries from various fields of the obtained parsed_calendar
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_SECOND , CALENDAR_SEC TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_MINUTE , CALENDAR_MIN TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_HOUR_OF_DAY , CALENDAR_HOUR TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_YEAR , CALENDAR_YEAR TSRMLS_CC); 
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_DAY_OF_MONTH , CALENDAR_MDAY TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_DAY_OF_WEEK  , CALENDAR_WDAY TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_DAY_OF_YEAR  , CALENDAR_YDAY TSRMLS_CC);
+       add_to_localtime_arr( mfo , return_value , parsed_calendar , UCAL_MONTH , CALENDAR_MON TSRMLS_CC);
+
+       //Is in DST?
+       isInDST = ucal_inDaylightTime(parsed_calendar    , &INTL_DATA_ERROR_CODE(mfo));
+        INTL_METHOD_CHECK_STATUS( mfo, "Date parsing - localtime failed : while checking if currently in DST." );
+       add_assoc_long( return_value, CALENDAR_ISDST ,(isInDST==1?1:0)); 
+}
+/* }}} */
+
+
+/* {{{ proto integer IntlDateFormatter::parse( string $text_to_parse  , int $parse_pos )
+ * Parse the string $value starting at parse_pos to a Unix timestamp -int }}}*/
+/* {{{ proto integer datefmt_parse( IntlDateFormatter $fmt, string $text_to_parse , int $parse_pos )
+ * Parse the string $value starting at parse_pos to a Unix timestamp -int }}}*/
+PHP_FUNCTION(datefmt_parse)
+{
+
+        char*           text_to_parse = NULL;
+        int32_t         text_len =0;
+        long           parse_pos =0;
+
+        DATE_FORMAT_METHOD_INIT_VARS;
+
+        // Parse parameters.
+        if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l",
+                &object, IntlDateFormatter_ce_ptr,  &text_to_parse ,  &text_len , &parse_pos ) == FAILURE ){
+                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                "datefmt_parse: unable to parse input params", 0 TSRMLS_CC );
+                        RETURN_FALSE;
+        }
+
+        // Fetch the object.
+        DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+       internal_parse_to_timestamp( mfo, text_to_parse ,  text_len , 
+               parse_pos , 
+               return_value TSRMLS_CC);
+
+}
+/* }}} */
+
+/* {{{ proto integer IntlDateFormatter::localtime( string $text_to_parse, int $parse_pos ))
+ * Parse the string $value to a localtime array  }}}*/
+/* {{{ proto integer datefmt_localtime( IntlDateFormatter $fmt, string $text_to_parse, int $parse_pos ))
+ * Parse the string $value to a localtime array  }}}*/
+PHP_FUNCTION(datefmt_localtime)
+{
+
+        char*           text_to_parse = NULL;
+        int32_t         text_len =0;
+        long           parse_pos =0;
+
+        DATE_FORMAT_METHOD_INIT_VARS;
+
+        // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osl",
+                &object, IntlDateFormatter_ce_ptr,  &text_to_parse ,  &text_len , &parse_pos ) == FAILURE ){
+
+                        intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+                                "datefmt_parse_to_localtime: unable to parse input params", 0 TSRMLS_CC );
+                        RETURN_FALSE;
+        }
+
+        // Fetch the object.
+        DATE_FORMAT_METHOD_FETCH_OBJECT;
+
+        internal_parse_to_localtime( mfo, text_to_parse ,  text_len , 
+               parse_pos, 
+               return_value TSRMLS_CC);
+
+}
+/* }}} */
+
diff --git a/ext/intl/dateformat/dateformat_parse.h b/ext/intl/dateformat/dateformat_parse.h
new file mode 100755 (executable)
index 0000000..c74a3d5
--- /dev/null
@@ -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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
+   +----------------------------------------------------------------------+
+*/
+#ifndef DATE_FORMAT_PARSE_H
+#define DATE_FORMAT_PARSE_H
+
+#include <php.h>
+
+PHP_FUNCTION( datefmt_parse );
+PHP_FUNCTION( datefmt_localtime );
+
+#endif // DATE_FORMAT_PARSE_H
diff --git a/ext/intl/doc/Tutorial.txt b/ext/intl/doc/Tutorial.txt
new file mode 100755 (executable)
index 0000000..4a66dc1
--- /dev/null
@@ -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 (executable)
index 0000000..38da279
--- /dev/null
@@ -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 (executable)
index 0000000..76ba9d8
--- /dev/null
@@ -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 (executable)
index 0000000..3724929
--- /dev/null
@@ -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 (executable)
index 0000000..c75596e
--- /dev/null
@@ -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 (executable)
index 0000000..57f4752
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+#############################################################################
+# Grapheme constants.
+#############################################################################
+
+       /**
+        * grapheme_extract extract_type
+        *
+       */
+       /** Extract the given number of whole grapheme clusters from the string: */
+       const GRAPHEME_EXTR_COUNT       = 0;
+       /** Extract as many whole grapheme clusters as will fit into the given number of bytes: */
+       const GRAPHEME_EXTR_MAXBYTES    = 1;
+       /** Extract whole grapheme clusters up to a maximum number of UTF-8 characters: */
+       const 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.
+       */
+       public 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 (executable)
index 0000000..b16e36a
--- /dev/null
@@ -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 (executable)
index 0000000..5d178f1
--- /dev/null
@@ -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 (executable)
index 0000000..a9bb566
--- /dev/null
@@ -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 (executable)
index 0000000..036974d
--- /dev/null
@@ -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 (executable)
index 0000000..1f9ebea
--- /dev/null
@@ -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 (executable)
index 0000000..c46bf05
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 <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" );
+
+       RETVAL_UNICODEL( 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 )
+{
+       long attribute;
+       UChar *value;
+       int len;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olu",
+               &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;
+
+       // Actually set new attribute value.
+       unum_setTextAttribute(FORMATTER_OBJECT(nfo), attribute, value, len, &INTL_DATA_ERROR_CODE(nfo));
+       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" );
+
+       RETVAL_UNICODEL( 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;
+       UChar*     value     = NULL;
+       int        value_len = 0;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olu",
+               &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;
+
+       // Actually set the symbol.
+       unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+       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" );
+
+       RETVAL_UNICODEL( 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 )
+{
+       UChar*      value = NULL;
+       int         value_len = 0;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ou",
+               &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;
+       }
+
+       // Fetch the object.
+       FORMATTER_METHOD_FETCH_OBJECT;
+
+       unum_applyPattern(FORMATTER_OBJECT(nfo), 0, value, value_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+       INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol 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 (executable)
index 0000000..c4bc94c
--- /dev/null
@@ -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 (executable)
index 0000000..776b9d1
--- /dev/null
@@ -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 (executable)
index 0000000..cf1cb06
--- /dev/null
@@ -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 (executable)
index 0000000..88f122f
--- /dev/null
@@ -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 (executable)
index 0000000..adc4818
--- /dev/null
@@ -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 (executable)
index 0000000..94232d4
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ 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+1);
+                               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+1);
+                               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+1);
+                               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;
+       }
+
+    formatted[formatted_len] = '\0';
+       RETVAL_UNICODEL( 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);
+       UChar*     currency      = NULL;
+       int        currency_len;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Odu",
+               &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;
+
+       // Format the number using a fixed-length buffer.
+       formatted_len = unum_formatDoubleCurrency(nfo->nf_data.unum, number, currency, 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+1);
+               unum_formatDoubleCurrency(nfo->nf_data.unum, number, currency, 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 {
+               formatted[formatted_len] = 0;
+               RETVAL_UNICODEL( formatted, formatted_len, ( formatted == format_buf ) );
+       }
+}
+
+/* }}} */
+
+/*
+ * 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 (executable)
index 0000000..35fafd1
--- /dev/null
@@ -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 (executable)
index 0000000..54f3f17
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ 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;
+       UChar*      pattern = NULL;
+       int         locale_len = 0, pattern_len = 0;
+       long        style;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|u",
+               &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;
+
+       if(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       FORMATTER_OBJECT(nfo) = unum_open(style, pattern, pattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+       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;
+       UChar*      pattern = NULL;
+       int         locale_len = 0, pattern_len = 0;
+       long        style;
+       FORMATTER_METHOD_INIT_VARS;
+
+       object = getThis();
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|u",
+               &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;
+
+       if(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       // Create an ICU number formatter.
+       FORMATTER_OBJECT(nfo) = unum_open(style, pattern, pattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+       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;
+       }
+
+       // Create an ICU number formatter.
+       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.c.orig b/ext/intl/formatter/formatter_main.c.orig
new file mode 100755 (executable)
index 0000000..fe5d9ac
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ 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;
+       UChar*      pattern = NULL;
+       int         locale_len = 0, style, pattern_len = 0;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|u",
+               &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;
+
+       if(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       FORMATTER_OBJECT(nfo) = unum_open(style, pattern, pattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+       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;
+       UChar*      pattern = NULL;
+       int         locale_len = 0, style, pattern_len = 0;
+       FORMATTER_METHOD_INIT_VARS;
+
+       object = getThis();
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|u",
+               &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(locale_len);
+       FORMATTER_METHOD_FETCH_OBJECT;
+
+       if(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       // Create an ICU number formatter.
+       FORMATTER_OBJECT(nfo) = unum_open(style, pattern, pattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+       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;
+       }
+
+       // Create an ICU number formatter.
+       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 (executable)
index 0000000..7669e68
--- /dev/null
@@ -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 (executable)
index 0000000..0d81e51
--- /dev/null
@@ -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/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+#include "formatter_parse.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* 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(), "Ou|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;
+
+       if(zposition) {
+               convert_to_long(zposition);
+               position = Z_LVAL_P( zposition );
+               position_p = &position;
+       }
+
+       switch(type) {
+               case FORMAT_TYPE_INT32:
+                       val32 = unum_parse(FORMATTER_OBJECT(nfo), str, str_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+                       RETVAL_LONG(val32);
+                       break;
+               case FORMAT_TYPE_INT64:
+                       val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), str, str_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+                       if(val64 > LONG_MAX || val64 < -LONG_MAX) {
+                               RETVAL_DOUBLE((double)val64);
+                       } else {
+                               val32 = (int32_t)val64;
+                               RETVAL_LONG(val32);
+                       }
+                       break;
+               case FORMAT_TYPE_DOUBLE:
+                       val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), str, str_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);
+       }
+
+       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 = 0;
+       UChar currency[5] = {0};
+       UChar *str = NULL;
+       int str_len = 0;
+       int32_t* position_p = NULL;
+       int32_t position = 0;
+       zval *zcurrency = NULL, *zposition = NULL;
+       FORMATTER_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ouz|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;
+
+       if(zposition) {
+               convert_to_long(zposition);
+               position = Z_LVAL_P( zposition );
+               position_p = &position;
+       }
+
+       number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), str, str_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
+       if(zposition) {
+               zval_dtor(zposition);
+               ZVAL_LONG(zposition, position);
+       }
+       INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+
+       zval_dtor( zcurrency );
+       ZVAL_UNICODE( zcurrency, currency, TRUE );
+
+       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 (executable)
index 0000000..cb96c72
--- /dev/null
@@ -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 (executable)
index 0000000..c0e697a
--- /dev/null
@@ -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 (executable)
index 0000000..9b4ba82
--- /dev/null
@@ -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 (executable)
index 0000000..375c695
--- /dev/null
@@ -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 (executable)
index 0000000..f8207ca
--- /dev/null
@@ -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 (executable)
index 0000000..d3d3222
--- /dev/null
@@ -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_USTRVAL_P(str)
+#define INTL_Z_STRLEN_P(str) Z_USTRLEN_P(str)
+
+#endif /* INTL_COMMON_H */
diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c
new file mode 100755 (executable)
index 0000000..1d3d0e6
--- /dev/null
@@ -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 (executable)
index 0000000..3fc03f4
--- /dev/null
@@ -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 (executable)
index 0000000..2c15a4a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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_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 (executable)
index 0000000..7c1df8e
--- /dev/null
@@ -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 (executable)
index 0000000..5c469e1
--- /dev/null
@@ -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 (executable)
index 0000000..eceb060
--- /dev/null
@@ -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_CONS_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 (executable)
index 0000000..ad3f9c6
--- /dev/null
@@ -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 (executable)
index 0000000..329ce73
--- /dev/null
@@ -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 (executable)
index 0000000..94232f3
--- /dev/null
@@ -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 (executable)
index 0000000..fa4de5a
--- /dev/null
@@ -0,0 +1,1750 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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( UG(default_locale) == NULL ) {
+               UG(default_locale) = pestrdup( uloc_getDefault(), 1);
+       }
+       RETURN_STRING( UG(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 =  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);
+               } else if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+                       buflen = uloc_getLanguage ( mod_loc_name ,tag_value , tag_value_len , &status);
+               } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
+                       buflen = uloc_getCountry ( mod_loc_name ,tag_value , tag_value_len , &status);
+               } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
+                       buflen = uloc_getVariant ( mod_loc_name ,tag_value , tag_value_len , &status);
+               } else 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 = UG(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*       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 = UG(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(UG(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 );
+       }
+
+       RETURN_UNICODEL(disp_name, buflen, 0);
+}
+/* }}} */
+
+/* {{{
+* 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 = UG(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 );
+}
+/* }}} */
+
+#define FREE_VALUE() if(Z_TYPE_PP(ele_value) == IS_UNICODE) { efree(value); }
+/* {{{ 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 ||
+                       zend_ascii_hash_find( hash_arr , key_name , strlen(key_name) + 1 , (void **)&ele_value ) == SUCCESS) {
+                               char *value;
+                               int value_len;
+
+                if( Z_TYPE_PP(ele_value) == IS_UNICODE ){
+                                       value = zend_unicode_to_ascii(Z_USTRVAL_PP(ele_value), Z_USTRLEN_PP(ele_value));
+                                       value_len = strlen(value);
+                               } else if( Z_TYPE_PP(ele_value) == IS_STRING ) {
+                                       value = Z_STRVAL_PP(ele_value);
+                                       value_len = Z_STRLEN_PP(ele_value);
+                               } else {
+                        //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 = value_len+1 ;
+
+                        if( needed_size > loc_name_capacity ){
+                                //Will cause Buffer_overflow
+                                                               FREE_VALUE();
+                                                               return -2;
+
+                        } else {
+                                strcat( loc_name , SEPARATOR);
+                                strncat( loc_name , value, value_len);
+                        }
+                } else {
+                        //lang or grandfathered  tag
+                        needed_size = value_len;
+                        if( needed_size > loc_name_capacity ){
+                           //Will cause Buffer_overflow
+                                                       FREE_VALUE();
+                            return -2;
+                        } else {
+                            strncat( loc_name , value, value_len );
+                        }
+                }
+
+                FREE_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 ||
+                       zend_ascii_hash_find( hash_arr , key_name , strlen(key_name) + 1 , (void **)&ele_value ) == SUCCESS){
+                               char *value;
+                               int value_len;
+
+                if( Z_TYPE_PP(ele_value) == IS_UNICODE ){
+                                       value = zend_unicode_to_ascii(Z_USTRVAL_PP(ele_value), Z_USTRLEN_PP(ele_value));
+                                       value_len = strlen(value);
+                               } else if( Z_TYPE_PP(ele_value) == IS_STRING ) {
+                                       value = Z_STRVAL_PP(ele_value);
+                                       value_len = Z_STRLEN_PP(ele_value);
+                               } else {
+                        //element value 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 = value_len + 3;
+                } else {
+                        //for the SEPARATOR and the size of array element
+                        needed_size = value_len + 1;
+                }
+                if( needed_size > loc_name_capacity){
+                        //Will cause buffer_overflow
+                               FREE_VALUE();
+                        return -2;
+                }
+
+                add_prefix( loc_name , key_name);
+
+                strcat( loc_name , SEPARATOR);
+                strncat( loc_name , value , value_len );
+                               FREE_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 ||
+                                                       zend_ascii_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS){
+                                                       char *value;
+                                                       int value_len;
+
+                                                       if( Z_TYPE_PP(ele_value) == IS_UNICODE ){
+                                                               value = zend_unicode_to_ascii(Z_USTRVAL_PP(ele_value), Z_USTRLEN_PP(ele_value));
+                                                               value_len = strlen(value);
+                                                       } else if( Z_TYPE_PP(ele_value) == IS_STRING ) {
+                                                               value = Z_STRVAL_PP(ele_value);
+                                                               value_len = Z_STRLEN_PP(ele_value);
+                                                       } else {
+                                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 = value_len + 3;
+                            } else {
+                                    //for the SEPARATOR and the size of array element
+                                    needed_size = value_len + 1;
+                            }
+                            if( needed_size > loc_name_capacity){
+                                    //Will cause buffer_overflow
+                                    if( cur_key_name){
+                                            efree(cur_key_name);
+                                    }
+                                                                       FREE_VALUE();
+                                    return -2;
+                            }
+
+                            //Add the contents
+                            if (isFirstSubtag++ == 0){
+                                    add_prefix( loc_name , cur_key_name);
+                            }
+                            strcat( loc_name , SEPARATOR);
+                            strncat( loc_name , value , value_len );
+
+                                                       result = 1;
+                                                       FREE_VALUE();
+                        }
+                }//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 = 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 );
+                       }
+               }
+       } 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_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 = UG(default_locale);
+    }
+
+       array_init( return_value );
+
+       grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
+       if( grOffset >= 0 ){
+               add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG , loc_name , 1 );
+       } 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 = UG(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 hyphens 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 = UG(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 && ( strcmp(token,cur_lang_tag)==0) ){
+                       //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 && (strcmp(token,cur_lang_tag)==0) ){
+                       //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 hyphens 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( lang_tag ){
+                       efree( lang_tag );
+               }
+               if( Z_TYPE_PP(ele_value)== IS_UNICODE ) {
+                       lang_tag = zend_unicode_to_ascii(Z_USTRVAL_PP(ele_value), Z_USTRLEN_PP(ele_value));
+               } else if( Z_TYPE_PP(ele_value)== IS_STRING ) {
+                       lang_tag = estrdup(Z_STRVAL_PP(ele_value));
+               } else {
+                       //element value is not a string 
+                       intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_internal_src_php: array element is not a string ", 0 TSRMLS_CC );
+                       return NULL;
+               } 
+
+               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 = UG(default_locale);
+       }
+
+       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), TRUE );
+       if( result){
+               efree( 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
+ *can_loc_len
+*/
diff --git a/ext/intl/locale/locale_methods.h b/ext/intl/locale/locale_methods.h
new file mode 100755 (executable)
index 0000000..0b2db50
--- /dev/null
@@ -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 (executable)
index 0000000..badd1e2
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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_data.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;
+       int         locale_len = 0;
+       UChar*      spattern     = NULL;
+       int         spattern_len = 0;
+       zval*       object;
+       int free_pattern = 0;
+       MessageFormatter_object* mfo;
+
+       intl_error_reset( NULL TSRMLS_CC );
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "su",
+               &locale, &locale_len, &spattern, &spattern_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(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       (mfo)->mf_data.orig_format = eustrndup(spattern, spattern_len);
+       (mfo)->mf_data.orig_format_len = spattern_len;
+       
+       if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo), &free_pattern) != 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();
+       }
+
+       // Create an ICU message formatter.
+       MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+       if(free_pattern) {
+               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;
+       int         locale_len;
+       UChar*      spattern     = NULL;
+       int         spattern_len = 0;
+       zval*       object;
+       int free_pattern = 0;
+       MessageFormatter_object* mfo;
+
+       intl_error_reset( NULL TSRMLS_CC );
+
+       object = getThis();
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "su",
+               &locale, &locale_len, &spattern, &spattern_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 );
+
+       if(locale_len == 0) {
+               locale = UG(default_locale);
+       }
+
+       (mfo)->mf_data.orig_format = eustrndup(spattern, spattern_len);
+       (mfo)->mf_data.orig_format_len = spattern_len;
+       
+       if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo), &free_pattern) != 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();
+       }
+
+       // Create an ICU message formatter.
+       MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+       if(free_pattern) {
+               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 (executable)
index 0000000..205c706
--- /dev/null
@@ -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 (executable)
index 0000000..e560b62
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: 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 <unicode/ustring.h>
+
+
+/* {{{ proto unicode MessageFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string msgfmt_get_pattern( MessageFormatter $mf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( msgfmt_get_pattern )
+{
+       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_UNICODEL(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 )
+{
+       int         slength = 0;
+       UChar*      svalue  = NULL;
+       int free_pattern = 0;
+       MSG_FORMAT_METHOD_INIT_VARS;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ou",
+               &object, MessageFormatter_ce_ptr, &svalue, &slength ) == 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;
+
+       if(mfo->mf_data.orig_format) {
+               efree(mfo->mf_data.orig_format);
+       }
+       mfo->mf_data.orig_format = eustrndup(svalue, slength);
+       mfo->mf_data.orig_format_len = slength;
+
+       if(msfgotmat_fix_quotes(&svalue, &slength, &INTL_DATA_ERROR_CODE(mfo), &free_pattern) != 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), svalue, slength, NULL, &INTL_DATA_ERROR_CODE(mfo));
+       if(free_pattern) {
+               efree(svalue);
+       }
+       INTL_METHOD_CHECK_STATUS(mfo, "Error setting symbol value");
+
+
+       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 (executable)
index 0000000..898c445
--- /dev/null
@@ -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 (executable)
index 0000000..853cbdb
--- /dev/null
@@ -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 (executable)
index 0000000..0afa792
--- /dev/null
@@ -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 (executable)
index 0000000..cdb5412
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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, int *free_pattern) 
+{
+       *free_pattern = 0;
+       if(*spattern && *spattern_len && u_strchr(*spattern, (UChar)'\'')) {
+               UChar *npattern = eumalloc(2*(*spattern_len)+1);
+               uint32_t npattern_len;
+               npattern_len = umsg_autoQuoteApostrophe(*spattern, *spattern_len, npattern, 2*(*spattern_len)+1, ec);
+               if( U_FAILURE(*ec) )
+               {
+                       efree(npattern);                        
+                       return FAILURE;
+               }
+               npattern = eurealloc(npattern, npattern_len+1);
+               *spattern = npattern;
+               *spattern_len = npattern_len;
+               *free_pattern = 1;
+       }
+       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 (executable)
index 0000000..dd1d392
--- /dev/null
@@ -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;
+       UChar*                  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, int *free_pattern);
+
+#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 (executable)
index 0000000..c7533d5
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ */
+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" );
+       RETURN_UNICODEL(formatted, formatted_len, 0);
+}
+/* }}} */
+
+/* {{{ 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       *slocale = NULL;
+       int         slocale_len = 0;
+       int free_pattern = 0;
+       MessageFormatter_object mf = {0};
+       MessageFormatter_object *mfo = &mf;
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "sua",
+                 &slocale, &slocale_len, &spattern, &spattern_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(slocale_len == 0) {
+               slocale = UG(default_locale);
+       }
+
+       if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo), &free_pattern) != 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(free_pattern) {
+               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 (executable)
index 0000000..b74deab
--- /dev/null
@@ -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 (executable)
index 0000000..e48c290
--- /dev/null
@@ -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: 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"
+}
+
+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_unicode_ex(&args[i]);
+                               fargs[i].setString(Z_STRVAL_P(args[i]));
+                           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;
+
+               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);
+                       ZVAL_UNICODEL((*args)[i], (UChar *)temp.getBuffer(), temp.length(), 1);
+            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 (executable)
index 0000000..30c7e39
--- /dev/null
@@ -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 (executable)
index 0000000..3601cfd
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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"
+
+/* {{{ */
+static void msgfmt_do_parse(MessageFormatter_object *mfo, UChar *source, int src_len, zval *return_value TSRMLS_DC) 
+{
+       zval **fargs;
+       int count = 0;
+       int i;
+
+       umsg_parse_helper(MSG_FORMAT_OBJECT(mfo), &count, &fargs, source, src_len, &INTL_DATA_ERROR_CODE(mfo));
+       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 )
+{
+       UChar *source;
+       int source_len;
+       MSG_FORMAT_METHOD_INIT_VARS;
+
+
+       // Parse parameters.
+       if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ou",
+               &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::parse( 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       *slocale = NULL;
+       int         slocale_len = 0;
+       UChar      *source = NULL;
+       int         source_len = 0;
+       int free_pattern = 0;
+       MessageFormatter_object mf={0};
+       MessageFormatter_object *mfo = &mf;
+
+       // Parse parameters.
+       if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "suu",
+                 &slocale, &slocale_len, &spattern, &spattern_len, &source, &source_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(slocale_len == 0) {
+               slocale = UG(default_locale);
+       }
+
+       if(msfgotmat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo), &free_pattern) != 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(free_pattern) {
+               efree(spattern);
+       }
+
+       INTL_METHOD_CHECK_STATUS(mfo, "Creating message formatter failed");
+       msgfmt_do_parse(mfo, source, source_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 (executable)
index 0000000..a937235
--- /dev/null
@@ -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 (executable)
index 0000000..f069413
--- /dev/null
@@ -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 (executable)
index 0000000..eca9abe
--- /dev/null
@@ -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 (executable)
index 0000000..44844e8
--- /dev/null
@@ -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 (executable)
index 0000000..1d21b7a
--- /dev/null
@@ -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 (executable)
index 0000000..7105c64
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 )
+{
+       // form is optional, defaults to FORM_C
+       long                    form = NORMALIZER_DEFAULT;
+               
+       UChar*                  uinput = NULL;
+       int                     uinput_len = 0;
+       int                     expansion_factor = 1;
+       UErrorCode              status = U_ZERO_ERROR;
+               
+       UChar*                  uret_buf = NULL;
+       int                     uret_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(), "u|l",
+                               &uinput, &uinput_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
+        */
+
+       // 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 );
+               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 );
+                       RETURN_NULL();
+               }
+       }
+
+       // the buffer we actually used
+       uret_len = size_needed;
+       uret_buf[uret_len] = 0;
+       RETURN_UNICODEL(uret_buf, uret_len, 0);
+}
+/* }}} */
+
+/* {{{ 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 )
+{
+       // form is optional, defaults to FORM_C
+       long            form = NORMALIZER_DEFAULT;
+
+       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(), "u|l",
+                               &uinput, &uinput_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 
+        */
+
+       // test string
+       uret = unorm_isNormalizedWithOptions( uinput, uinput_len, form, (int32_t) 0 /* options */, &status);
+       
+       // 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 (executable)
index 0000000..41c31f7
--- /dev/null
@@ -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 (executable)
index 0000000..f247966
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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 <unicode/uloc.h>
+#include "php_ini.h"
+
+#include "common/common_error.h"
+
+#include <ext/standard/info.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_get_args, 0, 0, 1 )
+        ZEND_ARG_INFO( 0, locale )
+        ZEND_ARG_INFO( 0, in_locale )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_filter_args, 0, 0, 3 )
+        ZEND_ARG_INFO( 0, langtag )
+        ZEND_ARG_INFO( 0, range )
+        ZEND_ARG_INFO( 0, canonical )
+ZEND_END_ARG_INFO()
+
+static
+ZEND_BEGIN_ARG_INFO_EX( locale_lookup_args, 0, 0, 3 )
+        ZEND_ARG_INFO( 0, langtag )
+        ZEND_ARG_INFO( 0, locale )
+        ZEND_ARG_INFO( 0, canonical )
+        ZEND_ARG_INFO( 0, fallback )
+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()
+
+static
+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_get_args )
+       PHP_FE( locale_get_display_region, locale_get_args )
+       PHP_FE( locale_get_display_name, locale_get_args )
+       PHP_FE( locale_get_display_language, locale_get_args )
+       PHP_FE( locale_get_display_variant, locale_get_args )
+       PHP_FE( locale_get_all_variants, locale_1_arg )
+       PHP_FE( locale_canonicalize, locale_1_arg )
+       PHP_FE( locale_compose, locale_1_arg )
+       PHP_FE( locale_parse, locale_1_arg )
+       PHP_FE( locale_filter_matches, locale_filter_args )
+       PHP_FE( locale_lookup, locale_lookup_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 )
+
+       // 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 );
+
+       // 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 (executable)
index 0000000..04dbcfa
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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
+
+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 (executable)
index 0000000..e2fc194
--- /dev/null
@@ -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 (executable)
index 0000000..e380380
--- /dev/null
@@ -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 (executable)
index 0000000..f302e51
--- /dev/null
@@ -0,0 +1,248 @@
+--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 )
+    {
+        // Convert strings to UTF-16 if needed.
+        $u_test_array = u( $test_array );
+
+        // Try to sort test data.
+        $res_val = ut_coll_asort( $coll, $u_test_array, $sort_flag );
+
+        // Return output data.
+        $res_dump = "\n" . dump_array( $u_test_array ) .
+                    "\n Result: " . dump_str( $res_val );
+
+               // Preppend test signature to output string
+        if( unicode_semantics() )
+             $md5 = md5( unicode_encode( $res_dump, 'utf-8' ) );
+        else
+             $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 (executable)
index 0000000..f3f7eba
--- /dev/null
@@ -0,0 +1,138 @@
+--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;
+
+        // Convert both strings to UTF-16 if needed.
+        $str1 = u( $str1 );
+        $str2 = u( $str2 );
+
+        // Compare strings.
+        $res_val = cmp_to_char( ut_coll_compare( $coll, $str1, $str2 ) );
+
+        // Concatenate result strings.
+        $res_str .= dump_str( $str1 ) .
+                    ' ' . $res_val . ' ' .
+                    dump_str( $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 (executable)
index 0000000..4a04d3a
--- /dev/null
@@ -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 (executable)
index 0000000..45a8e71
--- /dev/null
@@ -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 (executable)
index 0000000..af4b9c3
--- /dev/null
@@ -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 (executable)
index 0000000..2fab35e
--- /dev/null
@@ -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 (executable)
index 0000000..b234790
--- /dev/null
@@ -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 (executable)
index 0000000..1f77944
--- /dev/null
@@ -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 (executable)
index 0000000..f336d27
--- /dev/null
@@ -0,0 +1,253 @@
+--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 )
+    {
+        // Convert strings to UTF-16 if needed.
+        $u_array = u( $array );
+
+        // Sort array values
+        $res_val = ut_coll_sort( $coll, $u_array, $sort_flag );
+
+        // Concatenate the sorted array and function result
+        // with output string.
+        $res_dump = "\n" . dump_array( $u_array ) .
+                    "\n Result: " . dump_str( $res_val );
+
+               // Preppend test signature to output string
+        if( unicode_semantics() )
+             $md5 = md5( unicode_encode( $res_dump, 'utf-8' ) );
+        else
+             $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 (executable)
index 0000000..71afca3
--- /dev/null
@@ -0,0 +1,195 @@
+--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 )
+    {
+        // Convert strings to UTF-16 if needed.
+        $u_array = u( $array );
+
+        // Sort array values
+        $res_val = ut_coll_sort_with_sort_keys( $coll, $u_array );
+
+        // Concatenate the sorted array and function result
+        // with output string.
+        $res_dump = "\n" . dump_array( $u_array ) .
+                    "\n Result: " . dump_str( $res_val );
+        
+        
+        // Preppend test signature to output string
+        if( unicode_semantics() )
+             $md5 = md5( unicode_encode( $res_dump, 'utf-8' ) );
+        else
+             $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 (executable)
index 0000000..e8e661c
--- /dev/null
@@ -0,0 +1,298 @@
+--TEST--
+datefmt_format_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..b0e55a0
--- /dev/null
@@ -0,0 +1,308 @@
+--TEST--
+datefmt_format_code() and datefmt_parse_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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' ,
diff --git a/ext/intl/tests/dateformat_get_datetype.phpt b/ext/intl/tests/dateformat_get_datetype.phpt
new file mode 100755 (executable)
index 0000000..7ad79df
--- /dev/null
@@ -0,0 +1,58 @@
+--TEST--
+datefmt_get_datetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..b458c6e
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+datefmt_get_locale_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..67dd224
--- /dev/null
@@ -0,0 +1,61 @@
+--TEST--
+datefmt_get_calendar_code() datefmt_set_calendar_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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
+-------------------
diff --git a/ext/intl/tests/dateformat_get_set_pattern.phpt b/ext/intl/tests/dateformat_get_set_pattern.phpt
new file mode 100755 (executable)
index 0000000..2c1b78d
--- /dev/null
@@ -0,0 +1,85 @@
+--TEST--
+datefmt_get_pattern_code and datefmt_set_pattern_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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
diff --git a/ext/intl/tests/dateformat_get_timetype.phpt b/ext/intl/tests/dateformat_get_timetype.phpt
new file mode 100755 (executable)
index 0000000..014b1b2
--- /dev/null
@@ -0,0 +1,58 @@
+--TEST--
+datefmt_get_timetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..c926473
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+datefmt_get_timezone_id_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..531765d
--- /dev/null
@@ -0,0 +1,89 @@
+--TEST--
+datefmt_set_lenient and datefmt_set_lenient()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..0b2d362
--- /dev/null
@@ -0,0 +1,134 @@
+--TEST--
+datefmt_localtime_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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
diff --git a/ext/intl/tests/dateformat_parse.phpt b/ext/intl/tests/dateformat_parse.phpt
new file mode 100755 (executable)
index 0000000..ba0e357
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+datefmt_parse_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..ea761cf
--- /dev/null
@@ -0,0 +1,120 @@
+--TEST--
+datefmt_parse_localtime() with parse pos
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..8e8364b
--- /dev/null
@@ -0,0 +1,138 @@
+--TEST--
+datefmt_parse_timestamp_code()  with parse pos
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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 (executable)
index 0000000..6c3cbde
--- /dev/null
@@ -0,0 +1,76 @@
+--TEST--
+datefmt_set_timezone_id_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php 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
diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt
new file mode 100755 (executable)
index 0000000..b435a57
--- /dev/null
@@ -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 binary 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 binary 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 binary 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'
diff --git a/ext/intl/tests/formatter_format.phpt b/ext/intl/tests/formatter_format.phpt
new file mode 100755 (executable)
index 0000000..fb89362
--- /dev/null
@@ -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_str( 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 (executable)
index 0000000..e1d25ef
--- /dev/null
@@ -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 (executable)
index 0000000..94f0225
--- /dev/null
@@ -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: " . dump_str( ut_nfmt_format_currency( $fmt, $number, $currency ) ) . "\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 (executable)
index 0000000..c7b3972
--- /dev/null
@@ -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 (executable)
index 0000000..1a9b780
--- /dev/null
@@ -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 (executable)
index 0000000..c523346
--- /dev/null
@@ -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 (executable)
index 0000000..89f45a3
--- /dev/null
@@ -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 (executable)
index 0000000..0b4f848
--- /dev/null
@@ -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 = dump_str( ut_nfmt_get_symbol( $fmt, $symb ), false );
+        $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 = dump_str( ut_nfmt_get_symbol( $fmt, $symb ), false );
+        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 (executable)
index 0000000..6ad4d31
--- /dev/null
@@ -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 = dump_str( ut_nfmt_get_text_attribute( $fmt, $attr ), false );
+        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 = dump_str( ut_nfmt_get_text_attribute( $fmt, $attr ), false );
+        $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 (executable)
index 0000000..2ab9cce
--- /dev/null
@@ -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 (executable)
index 0000000..4f032d1
--- /dev/null
@@ -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 (executable)
index 0000000..b5c0bdb
--- /dev/null
@@ -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 (executable)
index 0000000..8f5b2dc
--- /dev/null
@@ -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 (executable)
index 0000000..6cd361b
--- /dev/null
@@ -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 (executable)
index 0000000..f81b5c0
--- /dev/null
@@ -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 (executable)
index 0000000..e07df8f
--- /dev/null
@@ -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 (executable)
index 0000000..345566a
--- /dev/null
@@ -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
+------------
diff --git a/ext/intl/tests/locale_filter_matches.phpt b/ext/intl/tests/locale_filter_matches.phpt
new file mode 100755 (executable)
index 0000000..19e760e
--- /dev/null
@@ -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 (executable)
index 0000000..864b8a2
--- /dev/null
@@ -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 (executable)
index 0000000..57c46cc
--- /dev/null
@@ -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 (executable)
index 0000000..f09dc2b
--- /dev/null
@@ -0,0 +1,282 @@
+--TEST--
+locale_get_display_language()
+--INI--
+unicode.runtime_encoding="utf-8"
+--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 (executable)
index 0000000..d2da8aa
--- /dev/null
@@ -0,0 +1,342 @@
+--TEST--
+locale_get_display_name()
+--INI--
+unicode.runtime_encoding="utf-8"
+--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 (executable)
index 0000000..166601e
--- /dev/null
@@ -0,0 +1,276 @@
+--TEST--
+locale_get_display_region()
+--INI--
+unicode.runtime_encoding="utf-8"
+--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 (executable)
index 0000000..93dcd36
--- /dev/null
@@ -0,0 +1,276 @@
+--TEST--
+locale_get_display_script()
+--INI--
+unicode.runtime_encoding="utf-8"
+--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=
+-----------------
diff --git a/ext/intl/tests/locale_get_display_variant.phpt b/ext/intl/tests/locale_get_display_variant.phpt
new file mode 100755 (executable)
index 0000000..4440a65
--- /dev/null
@@ -0,0 +1,276 @@
+--TEST--
+locale_get_display_variant()
+--INI--
+unicode.runtime_encoding="utf-8"
+--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 (executable)
index 0000000..5dec001
--- /dev/null
@@ -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 (executable)
index 0000000..bc92e92
--- /dev/null
@@ -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 (executable)
index 0000000..e62855e
--- /dev/null
@@ -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 (executable)
index 0000000..6a794fd
--- /dev/null
@@ -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 (executable)
index 0000000..08f92ff
--- /dev/null
@@ -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 (executable)
index 0000000..e670b31
--- /dev/null
@@ -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 (executable)
index 0000000..7debf4d
--- /dev/null
@@ -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 (executable)
index 0000000..ec28700
--- /dev/null
@@ -0,0 +1,100 @@
+--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("whatever", "{0,whatever}"),
+       array(array(), array()),
+       array("en", "{0,choice}"),
+       array("fr", "{0,"),
+       array("en_US", b"\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'
+
+Warning: MessageFormatter::__construct() expects parameter 1 to be binary 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 binary 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 binary 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'
+
+Warning: Could not convert binary string to Unicode string (converter UTF-8 failed on bytes (0xD0) at offset 0) in %s on line %d
+
+Warning: Could not convert binary string to Unicode string (converter UTF-8 failed on bytes (0xD0) at offset 0) in %s on line %d
+
+Warning: Could not convert binary string to Unicode string (converter UTF-8 failed on bytes (0xD0) at offset 0) in %s on line %d
diff --git a/ext/intl/tests/msgfmt_format.phpt b/ext/intl/tests/msgfmt_format.phpt
new file mode 100755 (executable)
index 0000000..4510353
--- /dev/null
@@ -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_str(intl_get_error_message())."\n";
+                       continue;
+               }
+        $str_res .= dump_str( ut_msgfmt_format( $fmt, array($m, $t, $m/$t) ) ) . "\n";
+               $str_res .= dump_str( 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 (executable)
index 0000000..015c50d
--- /dev/null
@@ -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 (executable)
index 0000000..4c2651f
--- /dev/null
@@ -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 (executable)
index 0000000..dd6fb5f
--- /dev/null
@@ -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 (executable)
index 0000000..5b5fc5f
--- /dev/null
@@ -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_str(intl_get_error_message())."\n";
+                       continue;
+               }
+        $str_res .= dump_str( ut_msgfmt_parse( $fmt, $results[$locale] ) ) . "\n";
+               $str_res .= dump_str( 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 (executable)
index 0000000..5fa25c7
--- /dev/null
@@ -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 = b"\xC3\xA4";        // 'LATIN SMALL LETTER A WITH DIAERESIS' (U+00E4)
+       $char_a_ring = b"\xC3\xA5";             // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
+       $char_o_diaeresis = b"\xC3\xB6";    // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
+
+       $char_angstrom_sign = b"\xE2\x84\xAB"; // 'ANGSTROM SIGN' (U+212B)
+       $char_A_ring = b"\xC3\x85";     // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
+
+       $char_ohm_sign = b"\xE2\x84\xA6";       // 'OHM SIGN' (U+2126)
+       $char_omega = b"\xCE\xA9";  // 'GREEK CAPITAL LETTER OMEGA' (U+03A9)
+
+       $char_combining_ring_above = b"\xCC\x8A";  // 'COMBINING RING ABOVE' (U+030A)
+
+       $char_fi_ligature = b"\xEF\xAC\x81";  // 'LATIN SMALL LIGATURE FI' (U+FB01)
+
+       $char_long_s_dot = b"\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((binary)$str);
+                       $str_norm_hex = urlencode((binary)$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 (executable)
index 0000000..83d50ad
--- /dev/null
@@ -0,0 +1,88 @@
+--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 );
+
+    // Convert strings to UTF-16 if needed.
+    $u_test_array = u( $test_array );
+
+    // Create two copies of the given array.
+    $u_copy1 = $u_test_array;
+    $u_copy2 = $u_test_array;
+
+    // Sort given array and the first copy of it.
+    ut_coll_sort( $coll, $u_test_array );
+    ut_coll_sort( $coll, $u_copy1      );
+
+    // Return contents of all the arrays.
+    // The second copy should remain unsorted.
+    $res_str .= dump_array( $u_test_array ) . "\n";
+    $res_str .= dump_array( $u_copy1 ) . "\n";
+    $res_str .= dump_array( $u_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 (executable)
index 0000000..449562e
--- /dev/null
@@ -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_array( $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 (executable)
index 0000000..143ba9a
--- /dev/null
@@ -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_array( $test_array ) . "\n";
+    $res_str .= dump_array( $copy1      ) . "\n";
+    $res_str .= dump_array( $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 (executable)
index 0000000..437edc1
--- /dev/null
@@ -0,0 +1,54 @@
+--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 );
+
+    // Convert strings to UTF-16 if needed.
+    $u_test_array = u( $test_array );
+
+    // Sort array.
+    ut_coll_sort_with_sort_keys( $coll, $u_test_array );
+
+    // And return the sorted array.
+    return dump_array( $u_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 (executable)
index 0000000..bd4f13b
--- /dev/null
@@ -0,0 +1,461 @@
+<?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 );
+}
+/*
+ * Convert a binary string content of $var to unicode.
+ */
+function u( $var )
+{
+    if( is_string( $var ) )
+        return u_str( $var );
+
+    if( is_array( $var ) )
+        return u_array( $var );
+
+    return $var;
+}
+
+/*
+ * Convert a binary string to unicode one.
+ */
+function u_str( $s )
+{
+    if( !is_binary( $s ) )
+        return $s;
+
+    return unicode_decode( $s, 'utf-8' );
+}
+
+/*
+ * Convert each binary string item of array to unicode string.
+ */
+function u_array( $a )
+{
+    $b = array();
+    foreach( $a as $key => $val )
+        $b[$key] = u( $val );
+
+    return $b;
+}
+
+/*
+ * Same as 'var_export" but does conversion binary string content
+ * of $str to utf-8.
+ */
+function dump_str( $val, $use_quotes = true )
+{
+    $q = '';
+    if( $use_quotes )
+       $q = "'";
+    if( is_unicode( $val ) && !unicode_semantics() )
+        return $q . unicode_encode( $val, 'utf-8' ) . $q;
+
+    if( is_string( $val ) )
+        return $q . "$val" . $q;
+
+    return var_export( $val, true );
+}
+
+/*
+ * Same as 'var_export" but does conversion binary string content
+ * of $str to utf-8.
+ */
+function dump_array( $a )
+{
+    $b = "array (\n";
+    foreach( $a as $key => $val )
+    {
+       if( is_integer( $key ) )
+            $b .= "  $key => ";
+        else
+            $b .= "  '$key' => ";
+
+        if( is_unicode( $val ) && !unicode_semantics() )
+           $b .= "'" . unicode_encode( $val, 'utf-8' ) . "'";
+        elseif( is_null( $val ) )
+            $b .= "NULL";
+        elseif( is_string( $val ) )
+            $b .= "'" . "$val" . "'";
+        else
+            $b .= $val;
+        $b .= ",\n";
+    }
+    $b .= ")";
+
+    return $b;
+}
+
+/*
+ * 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_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 );
+}
+/* DateFormatter 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 );
+}
+?>