summaryrefslogtreecommitdiff
path: root/ext/intl
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /ext/intl
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/intl')
-rw-r--r--ext/intl/CREDITS2
-rw-r--r--ext/intl/TODO6
-rw-r--r--ext/intl/collator/collator.c96
-rw-r--r--ext/intl/collator/collator.h29
-rw-r--r--ext/intl/collator/collator_attr.c157
-rw-r--r--ext/intl/collator/collator_attr.h28
-rw-r--r--ext/intl/collator/collator_class.c202
-rw-r--r--ext/intl/collator/collator_class.h69
-rw-r--r--ext/intl/collator/collator_compare.c133
-rw-r--r--ext/intl/collator/collator_compare.h25
-rw-r--r--ext/intl/collator/collator_convert.c485
-rw-r--r--ext/intl/collator/collator_convert.h38
-rw-r--r--ext/intl/collator/collator_create.c87
-rw-r--r--ext/intl/collator/collator_create.h27
-rw-r--r--ext/intl/collator/collator_error.c94
-rw-r--r--ext/intl/collator/collator_error.h26
-rw-r--r--ext/intl/collator/collator_is_numeric.c305
-rw-r--r--ext/intl/collator/collator_is_numeric.h26
-rw-r--r--ext/intl/collator/collator_locale.c80
-rw-r--r--ext/intl/collator/collator_locale.h25
-rw-r--r--ext/intl/collator/collator_sort.c621
-rw-r--r--ext/intl/collator/collator_sort.h30
-rw-r--r--ext/intl/common/common_error.c269
-rw-r--r--ext/intl/common/common_error.h30
-rw-r--r--ext/intl/config.m482
-rw-r--r--ext/intl/config.w3295
-rw-r--r--ext/intl/dateformat/dateformat.c272
-rw-r--r--ext/intl/dateformat/dateformat.h45
-rw-r--r--ext/intl/dateformat/dateformat_attr.c412
-rw-r--r--ext/intl/dateformat/dateformat_attr.h34
-rw-r--r--ext/intl/dateformat/dateformat_class.c208
-rw-r--r--ext/intl/dateformat/dateformat_class.h52
-rw-r--r--ext/intl/dateformat/dateformat_data.c62
-rw-r--r--ext/intl/dateformat/dateformat_data.h37
-rw-r--r--ext/intl/dateformat/dateformat_format.c194
-rw-r--r--ext/intl/dateformat/dateformat_format.h24
-rw-r--r--ext/intl/dateformat/dateformat_parse.c199
-rw-r--r--ext/intl/dateformat/dateformat_parse.h24
-rw-r--r--ext/intl/doc/Tutorial.txt239
-rw-r--r--ext/intl/doc/collator_api.php397
-rw-r--r--ext/intl/doc/common_api.php56
-rw-r--r--ext/intl/doc/datefmt_api.php436
-rw-r--r--ext/intl/doc/formatter_api.php500
-rw-r--r--ext/intl/doc/grapheme_api.php132
-rw-r--r--ext/intl/doc/locale_api.php434
-rw-r--r--ext/intl/doc/msgfmt_api.php209
-rw-r--r--ext/intl/doc/normalizer_api.php92
-rw-r--r--ext/intl/formatter/formatter.c152
-rw-r--r--ext/intl/formatter/formatter.h24
-rw-r--r--ext/intl/formatter/formatter_attr.c459
-rw-r--r--ext/intl/formatter/formatter_attr.h32
-rw-r--r--ext/intl/formatter/formatter_class.c226
-rw-r--r--ext/intl/formatter/formatter_class.h50
-rw-r--r--ext/intl/formatter/formatter_data.c72
-rw-r--r--ext/intl/formatter/formatter_data.h38
-rw-r--r--ext/intl/formatter/formatter_format.c204
-rw-r--r--ext/intl/formatter/formatter_format.h31
-rw-r--r--ext/intl/formatter/formatter_main.c156
-rw-r--r--ext/intl/formatter/formatter_main.h27
-rw-r--r--ext/intl/formatter/formatter_parse.c187
-rw-r--r--ext/intl/formatter/formatter_parse.h25
-rw-r--r--ext/intl/grapheme/grapheme.h37
-rw-r--r--ext/intl/grapheme/grapheme_string.c939
-rw-r--r--ext/intl/grapheme/grapheme_util.c642
-rw-r--r--ext/intl/grapheme/grapheme_util.h59
-rw-r--r--ext/intl/idn/idn.c351
-rw-r--r--ext/intl/idn/idn.h30
-rw-r--r--ext/intl/intl_common.h44
-rw-r--r--ext/intl/intl_convert.c157
-rw-r--r--ext/intl/intl_convert.h33
-rw-r--r--ext/intl/intl_data.h107
-rw-r--r--ext/intl/intl_error.c233
-rw-r--r--ext/intl/intl_error.h47
-rw-r--r--ext/intl/locale/locale.c72
-rw-r--r--ext/intl/locale/locale.h38
-rw-r--r--ext/intl/locale/locale_class.c121
-rw-r--r--ext/intl/locale/locale_class.h48
-rw-r--r--ext/intl/locale/locale_methods.c1610
-rw-r--r--ext/intl/locale/locale_methods.h49
-rw-r--r--ext/intl/msgformat/msgformat.c176
-rw-r--r--ext/intl/msgformat/msgformat.h27
-rw-r--r--ext/intl/msgformat/msgformat_attr.c145
-rw-r--r--ext/intl/msgformat/msgformat_attr.h26
-rw-r--r--ext/intl/msgformat/msgformat_class.c192
-rw-r--r--ext/intl/msgformat/msgformat_class.h55
-rw-r--r--ext/intl/msgformat/msgformat_data.c102
-rw-r--r--ext/intl/msgformat/msgformat_data.h44
-rw-r--r--ext/intl/msgformat/msgformat_format.c186
-rw-r--r--ext/intl/msgformat/msgformat_format.h25
-rw-r--r--ext/intl/msgformat/msgformat_helpers.cpp212
-rw-r--r--ext/intl/msgformat/msgformat_helpers.h25
-rw-r--r--ext/intl/msgformat/msgformat_parse.c161
-rw-r--r--ext/intl/msgformat/msgformat_parse.h25
-rw-r--r--ext/intl/normalizer/normalizer.c68
-rw-r--r--ext/intl/normalizer/normalizer.h37
-rw-r--r--ext/intl/normalizer/normalizer_class.c81
-rw-r--r--ext/intl/normalizer/normalizer_class.h43
-rw-r--r--ext/intl/normalizer/normalizer_normalize.c258
-rw-r--r--ext/intl/normalizer/normalizer_normalize.h25
-rw-r--r--ext/intl/php_intl.c733
-rw-r--r--ext/intl/php_intl.h77
-rw-r--r--ext/intl/resourcebundle/TODO1
-rw-r--r--ext/intl/resourcebundle/resourcebundle.c92
-rw-r--r--ext/intl/resourcebundle/resourcebundle.h28
-rw-r--r--ext/intl/resourcebundle/resourcebundle_class.c458
-rw-r--r--ext/intl/resourcebundle/resourcebundle_class.h58
-rw-r--r--ext/intl/resourcebundle/resourcebundle_iterator.c192
-rw-r--r--ext/intl/resourcebundle/resourcebundle_iterator.h36
-rw-r--r--ext/intl/spoofchecker/spoofchecker.c60
-rw-r--r--ext/intl/spoofchecker/spoofchecker.h24
-rw-r--r--ext/intl/spoofchecker/spoofchecker_class.c210
-rw-r--r--ext/intl/spoofchecker/spoofchecker_class.h76
-rw-r--r--ext/intl/spoofchecker/spoofchecker_create.c59
-rw-r--r--ext/intl/spoofchecker/spoofchecker_create.h24
-rw-r--r--ext/intl/spoofchecker/spoofchecker_main.c142
-rw-r--r--ext/intl/spoofchecker/spoofchecker_main.h27
-rw-r--r--ext/intl/tests/_files/es-bundle.txt21
-rw-r--r--ext/intl/tests/_files/res_index.txt6
-rw-r--r--ext/intl/tests/_files/resourcebundle.txt21
-rw-r--r--ext/intl/tests/_files/resourcebundle/es.resbin0 -> 384 bytes
-rw-r--r--ext/intl/tests/_files/resourcebundle/res_index.resbin0 -> 128 bytes
-rw-r--r--ext/intl/tests/_files/resourcebundle/root.resbin0 -> 388 bytes
-rw-r--r--ext/intl/tests/badargs.phpt25
-rw-r--r--ext/intl/tests/bug12887.phpt29
-rw-r--r--ext/intl/tests/bug14562.phpt28
-rw-r--r--ext/intl/tests/bug48227.phpt21
-rw-r--r--ext/intl/tests/bug50590.phpt13
-rw-r--r--ext/intl/tests/bug53512.phpt25
-rw-r--r--ext/intl/tests/bug55562.phpt13
-rw-r--r--ext/intl/tests/bug59597_32.phpt21
-rw-r--r--ext/intl/tests/bug59597_64.phpt20
-rw-r--r--ext/intl/tests/bug60192-compare.phpt19
-rw-r--r--ext/intl/tests/bug60192-getlocale.phpt20
-rw-r--r--ext/intl/tests/bug60192-getsortkey.phpt20
-rw-r--r--ext/intl/tests/bug60192-sort.phpt21
-rw-r--r--ext/intl/tests/bug60192-sortwithsortkeys.phpt21
-rw-r--r--ext/intl/tests/bug61487.phpt11
-rw-r--r--ext/intl/tests/bug62017.phpt22
-rw-r--r--ext/intl/tests/bug62070.phpt18
-rw-r--r--ext/intl/tests/bug62081.phpt14
-rw-r--r--ext/intl/tests/bug62082.phpt15
-rw-r--r--ext/intl/tests/bug62083.phpt12
-rw-r--r--ext/intl/tests/bug62915-2.phpt33
-rw-r--r--ext/intl/tests/collation_customization.phpt70
-rw-r--r--ext/intl/tests/collator_asort.phpt242
-rw-r--r--ext/intl/tests/collator_compare.phpt134
-rw-r--r--ext/intl/tests/collator_create.phpt82
-rw-r--r--ext/intl/tests/collator_create2.phpt82
-rw-r--r--ext/intl/tests/collator_get_error_code.phpt47
-rw-r--r--ext/intl/tests/collator_get_error_message.phpt36
-rw-r--r--ext/intl/tests/collator_get_locale.phpt51
-rw-r--r--ext/intl/tests/collator_get_locale2.phpt51
-rw-r--r--ext/intl/tests/collator_get_set_attribute.phpt41
-rw-r--r--ext/intl/tests/collator_get_set_strength.phpt41
-rw-r--r--ext/intl/tests/collator_get_sort_key.phpt98
-rw-r--r--ext/intl/tests/collator_sort.phpt247
-rw-r--r--ext/intl/tests/collator_sort_with_sort_keys.phpt189
-rw-r--r--ext/intl/tests/dateformat_calendars.phpt45
-rw-r--r--ext/intl/tests/dateformat_clone.phpt37
-rw-r--r--ext/intl/tests/dateformat_clone2.phpt37
-rw-r--r--ext/intl/tests/dateformat_clone_bad_obj.phpt20
-rw-r--r--ext/intl/tests/dateformat_format.phpt420
-rw-r--r--ext/intl/tests/dateformat_format_parse.phpt294
-rw-r--r--ext/intl/tests/dateformat_get_datetype.phpt57
-rw-r--r--ext/intl/tests/dateformat_get_locale.phpt59
-rw-r--r--ext/intl/tests/dateformat_get_set_calendar.phpt60
-rw-r--r--ext/intl/tests/dateformat_get_set_pattern.phpt85
-rw-r--r--ext/intl/tests/dateformat_get_set_pattern2.phpt85
-rw-r--r--ext/intl/tests/dateformat_get_timetype.phpt57
-rw-r--r--ext/intl/tests/dateformat_get_timezone_id.phpt49
-rw-r--r--ext/intl/tests/dateformat_is_set_lenient.phpt88
-rw-r--r--ext/intl/tests/dateformat_localtime.phpt134
-rw-r--r--ext/intl/tests/dateformat_parse.phpt113
-rw-r--r--ext/intl/tests/dateformat_parse_localtime_parsepos.phpt120
-rw-r--r--ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt157
-rw-r--r--ext/intl/tests/dateformat_set_timezone_id.phpt76
-rw-r--r--ext/intl/tests/dateformat_set_timezone_id2.phpt76
-rw-r--r--ext/intl/tests/formatter_clone.phpt30
-rw-r--r--ext/intl/tests/formatter_clone_bad_obj.phpt20
-rw-r--r--ext/intl/tests/formatter_fail.phpt79
-rw-r--r--ext/intl/tests/formatter_format.phpt130
-rw-r--r--ext/intl/tests/formatter_format2.phpt130
-rw-r--r--ext/intl/tests/formatter_format_conv.phpt24
-rw-r--r--ext/intl/tests/formatter_format_currency.phpt50
-rw-r--r--ext/intl/tests/formatter_format_currency2.phpt50
-rw-r--r--ext/intl/tests/formatter_get_error.phpt31
-rw-r--r--ext/intl/tests/formatter_get_locale.phpt49
-rw-r--r--ext/intl/tests/formatter_get_set_attribute.phpt194
-rw-r--r--ext/intl/tests/formatter_get_set_pattern.phpt52
-rw-r--r--ext/intl/tests/formatter_get_set_symbol.phpt190
-rw-r--r--ext/intl/tests/formatter_get_set_symbol2.phpt190
-rw-r--r--ext/intl/tests/formatter_get_set_text_attribute.phpt121
-rw-r--r--ext/intl/tests/formatter_parse.phpt42
-rw-r--r--ext/intl/tests/formatter_parse_currency.phpt37
-rw-r--r--ext/intl/tests/grapheme.phpt1192
-rw-r--r--ext/intl/tests/idn.phpt18
-rw-r--r--ext/intl/tests/idn_uts46_basic.phpt53
-rw-r--r--ext/intl/tests/idn_uts46_errors.phpt89
-rw-r--r--ext/intl/tests/intl_error_name.phpt25
-rw-r--r--ext/intl/tests/intl_get_error_code.phpt24
-rw-r--r--ext/intl/tests/intl_get_error_message.phpt21
-rw-r--r--ext/intl/tests/intl_icu_data_version_constant.phpt10
-rw-r--r--ext/intl/tests/intl_icu_version_constant.phpt10
-rw-r--r--ext/intl/tests/intl_is_failure.phpt25
-rw-r--r--ext/intl/tests/locale_accept.phpt46
-rw-r--r--ext/intl/tests/locale_compose_locale.phpt189
-rw-r--r--ext/intl/tests/locale_filter_matches.phpt366
-rw-r--r--ext/intl/tests/locale_filter_matches2.phpt366
-rw-r--r--ext/intl/tests/locale_get_all_variants.phpt62
-rw-r--r--ext/intl/tests/locale_get_default.phpt46
-rw-r--r--ext/intl/tests/locale_get_display_language.phpt280
-rw-r--r--ext/intl/tests/locale_get_display_name.phpt342
-rw-r--r--ext/intl/tests/locale_get_display_name2.phpt342
-rw-r--r--ext/intl/tests/locale_get_display_region.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_region2.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_script.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_script2.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_script3.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_variant.phpt275
-rw-r--r--ext/intl/tests/locale_get_display_variant2.phpt275
-rw-r--r--ext/intl/tests/locale_get_keywords.phpt140
-rw-r--r--ext/intl/tests/locale_get_keywords2.phpt151
-rw-r--r--ext/intl/tests/locale_get_primary_language.phpt121
-rw-r--r--ext/intl/tests/locale_get_region.phpt123
-rw-r--r--ext/intl/tests/locale_get_script.phpt122
-rw-r--r--ext/intl/tests/locale_lookup.phpt99
-rw-r--r--ext/intl/tests/locale_parse_locale.phpt203
-rw-r--r--ext/intl/tests/locale_parse_locale2.phpt203
-rw-r--r--ext/intl/tests/locale_set_default.phpt133
-rw-r--r--ext/intl/tests/msgfmt_clone.phpt30
-rw-r--r--ext/intl/tests/msgfmt_clone_bad_obj.phpt20
-rw-r--r--ext/intl/tests/msgfmt_fail.phpt102
-rw-r--r--ext/intl/tests/msgfmt_fail2.phpt102
-rw-r--r--ext/intl/tests/msgfmt_format.phpt70
-rw-r--r--ext/intl/tests/msgfmt_get_error.phpt29
-rw-r--r--ext/intl/tests/msgfmt_get_locale.phpt40
-rw-r--r--ext/intl/tests/msgfmt_get_set_pattern.phpt53
-rw-r--r--ext/intl/tests/msgfmt_parse.phpt114
-rw-r--r--ext/intl/tests/normalizer_normalize.phpt160
-rw-r--r--ext/intl/tests/regression_sort_and_cow.phpt85
-rw-r--r--ext/intl/tests/regression_sort_eq.phpt51
-rw-r--r--ext/intl/tests/regression_sortwsk_and_cow.phpt86
-rw-r--r--ext/intl/tests/regression_sortwsk_eq.phpt51
-rw-r--r--ext/intl/tests/resourcebundle.build33
-rw-r--r--ext/intl/tests/resourcebundle.inc13
-rw-r--r--ext/intl/tests/resourcebundle_arrayaccess.phpt50
-rw-r--r--ext/intl/tests/resourcebundle_create.phpt62
-rw-r--r--ext/intl/tests/resourcebundle_individual.phpt57
-rw-r--r--ext/intl/tests/resourcebundle_internal.phpt19
-rw-r--r--ext/intl/tests/resourcebundle_iterator.phpt73
-rw-r--r--ext/intl/tests/resourcebundle_locales.phpt23
-rw-r--r--ext/intl/tests/resourcebundle_null_mandatory_args.phpt28
-rw-r--r--ext/intl/tests/resourcebundle_traversable.phpt23
-rw-r--r--ext/intl/tests/spoofchecker_001.phpt23
-rw-r--r--ext/intl/tests/spoofchecker_002.phpt20
-rw-r--r--ext/intl/tests/spoofchecker_003.phpt25
-rw-r--r--ext/intl/tests/spoofchecker_004.phpt28
-rw-r--r--ext/intl/tests/symfony_format_type_double_intl1.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_double_intl2.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_double_intl3.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_double_intl4.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl1.phpt49
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl2.phpt33
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl3.phpt32
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl4.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl5.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl6.phpt32
-rw-r--r--ext/intl/tests/symfony_format_type_int32_intl7.phpt32
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl1.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl2.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl3.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl4.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl5.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl6.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl7.phpt30
-rw-r--r--ext/intl/tests/symfony_format_type_int64_intl8.phpt30
-rw-r--r--ext/intl/tests/transliterator_clone.phpt21
-rw-r--r--ext/intl/tests/transliterator_create_basic.phpt20
-rw-r--r--ext/intl/tests/transliterator_create_error.phpt21
-rw-r--r--ext/intl/tests/transliterator_create_from_rule_basic.phpt28
-rw-r--r--ext/intl/tests/transliterator_create_from_rule_error.phpt53
-rw-r--r--ext/intl/tests/transliterator_create_inverse_basic.phpt32
-rw-r--r--ext/intl/tests/transliterator_create_inverse_error.phpt21
-rw-r--r--ext/intl/tests/transliterator_get_error_code_basic.phpt25
-rw-r--r--ext/intl/tests/transliterator_get_error_code_error.phpt24
-rw-r--r--ext/intl/tests/transliterator_get_error_message_basic.phpt25
-rw-r--r--ext/intl/tests/transliterator_get_error_message_error.phpt24
-rw-r--r--ext/intl/tests/transliterator_list_ids_basic.phpt16
-rw-r--r--ext/intl/tests/transliterator_list_ids_error.phpt18
-rw-r--r--ext/intl/tests/transliterator_property_id.phpt21
-rw-r--r--ext/intl/tests/transliterator_transliterate_basic.phpt20
-rw-r--r--ext/intl/tests/transliterator_transliterate_error.phpt60
-rw-r--r--ext/intl/tests/transliterator_transliterate_variant1.phpt37
-rw-r--r--ext/intl/tests/ut_common.inc416
-rw-r--r--ext/intl/transliterator/transliterator.c138
-rw-r--r--ext/intl/transliterator/transliterator.h29
-rw-r--r--ext/intl/transliterator/transliterator_class.c434
-rw-r--r--ext/intl/transliterator/transliterator_class.h65
-rw-r--r--ext/intl/transliterator/transliterator_methods.c542
-rw-r--r--ext/intl/transliterator/transliterator_methods.h38
300 files changed, 33925 insertions, 0 deletions
diff --git a/ext/intl/CREDITS b/ext/intl/CREDITS
new file mode 100644
index 0000000..0e7fd55
--- /dev/null
+++ b/ext/intl/CREDITS
@@ -0,0 +1,2 @@
+Internationalization
+Ed Batutis, Vladimir Iordanov, Dmitry Lakhtyuk, Stanislav Malyshev, Vadim Savchuk, Kirti Velankar
diff --git a/ext/intl/TODO b/ext/intl/TODO
new file mode 100644
index 0000000..6fd1b27
--- /dev/null
+++ b/ext/intl/TODO
@@ -0,0 +1,6 @@
+- Unify __ctor and create functions
+- Document U_* error constants
+- For IntlDateFormatter
+-- Accept and produce DateTime objects in 5.3 and 6
+-- Create convertor from ICU pattern to PHP pattern
+
diff --git a/ext/intl/collator/collator.c b/ext/intl/collator/collator.c
new file mode 100644
index 0000000..047a738
--- /dev/null
+++ b/ext/intl/collator/collator.c
@@ -0,0 +1,96 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "collator_class.h"
+#include "collator.h"
+
+#include <unicode/utypes.h>
+#include <unicode/ucol.h>
+#include <unicode/ustring.h>
+
+/* {{{ collator_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void collator_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Collator_ce_ptr )
+ {
+ zend_error( E_ERROR, "Collator class not defined" );
+ return;
+ }
+
+ #define COLLATOR_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define COLLATOR_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( #x ) - 1, UCOL_##x TSRMLS_CC );
+ #define COLLATOR_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ /* UColAttributeValue constants */
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "DEFAULT_VALUE", UCOL_DEFAULT );
+
+ COLLATOR_EXPOSE_CLASS_CONST( PRIMARY );
+ COLLATOR_EXPOSE_CLASS_CONST( SECONDARY );
+ COLLATOR_EXPOSE_CLASS_CONST( TERTIARY );
+ COLLATOR_EXPOSE_CLASS_CONST( DEFAULT_STRENGTH );
+ COLLATOR_EXPOSE_CLASS_CONST( QUATERNARY );
+ COLLATOR_EXPOSE_CLASS_CONST( IDENTICAL );
+
+ COLLATOR_EXPOSE_CLASS_CONST( OFF );
+ COLLATOR_EXPOSE_CLASS_CONST( ON );
+
+ COLLATOR_EXPOSE_CLASS_CONST( SHIFTED );
+ COLLATOR_EXPOSE_CLASS_CONST( NON_IGNORABLE );
+
+ COLLATOR_EXPOSE_CLASS_CONST( LOWER_FIRST );
+ COLLATOR_EXPOSE_CLASS_CONST( UPPER_FIRST );
+
+ /* UColAttribute constants */
+ COLLATOR_EXPOSE_CLASS_CONST( FRENCH_COLLATION );
+ COLLATOR_EXPOSE_CLASS_CONST( ALTERNATE_HANDLING );
+ COLLATOR_EXPOSE_CLASS_CONST( CASE_FIRST );
+ COLLATOR_EXPOSE_CLASS_CONST( CASE_LEVEL );
+ COLLATOR_EXPOSE_CLASS_CONST( NORMALIZATION_MODE );
+ COLLATOR_EXPOSE_CLASS_CONST( STRENGTH );
+ COLLATOR_EXPOSE_CLASS_CONST( HIRAGANA_QUATERNARY_MODE );
+ COLLATOR_EXPOSE_CLASS_CONST( NUMERIC_COLLATION );
+
+ /* ULocDataLocaleType constants */
+ COLLATOR_EXPOSE_CONST( ULOC_ACTUAL_LOCALE );
+ COLLATOR_EXPOSE_CONST( ULOC_VALID_LOCALE );
+
+ /* sort flags */
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_REGULAR", COLLATOR_SORT_REGULAR );
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_STRING", COLLATOR_SORT_STRING );
+ COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_NUMERIC", COLLATOR_SORT_NUMERIC );
+
+ #undef COLLATOR_EXPOSE_CUSTOM_CLASS_CONST
+ #undef COLLATOR_EXPOSE_CLASS_CONST
+ #undef COLLATOR_EXPOSE_CONST
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator.h b/ext/intl/collator/collator.h
new file mode 100644
index 0000000..96e7aa0
--- /dev/null
+++ b/ext/intl/collator/collator.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_COLLATOR_H
+#define CCOLLATOR_COLLATOR_H
+
+#include <php.h>
+
+#define COLLATOR_SORT_REGULAR 0
+#define COLLATOR_SORT_STRING 1
+#define COLLATOR_SORT_NUMERIC 2
+
+void collator_register_constants( INIT_FUNC_ARGS );
+
+#endif // COLLATOR_COLLATOR_H
diff --git a/ext/intl/collator/collator_attr.c b/ext/intl/collator/collator_attr.c
new file mode 100644
index 0000000..684e72c
--- /dev/null
+++ b/ext/intl/collator/collator_attr.c
@@ -0,0 +1,157 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_convert.h"
+#include "collator_attr.h"
+
+#include <unicode/ustring.h>
+
+/* {{{ proto int Collator::getAttribute( int $attr )
+ * Get collation attribute value. }}} */
+/* {{{ proto int collator_get_attribute( Collator $coll, int $attr )
+ * Get collation attribute value.
+ */
+PHP_FUNCTION( collator_get_attribute )
+{
+ long attribute, value;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, Collator_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ value = ucol_getAttribute( co->ucoll, attribute, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error getting attribute value" );
+
+ RETURN_LONG( value );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::getAttribute( int $attr )
+ * Get collation attribute value. }}} */
+/* {{{ proto bool collator_set_attribute( Collator $coll, int $attr, int $val )
+ * Set collation attribute.
+ */
+PHP_FUNCTION( collator_set_attribute )
+{
+ long attribute, value;
+ COLLATOR_METHOD_INIT_VARS
+
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll",
+ &object, Collator_ce_ptr, &attribute, &value ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_set_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ /* Set new value for the given attribute. */
+ ucol_setAttribute( co->ucoll, attribute, value, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error setting attribute value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int Collator::getStrength()
+ * Returns the current collation strength. }}} */
+/* {{{ proto int collator_get_strength(Collator coll)
+ * Returns the current collation strength.
+ */
+PHP_FUNCTION( collator_get_strength )
+{
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_strength: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ /* Get current strength and return it. */
+ RETURN_LONG( ucol_getStrength( co->ucoll ) );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::setStrength(int strength)
+ * Set the collation strength. }}} */
+/* {{{ proto bool collator_set_strength(Collator coll, int strength)
+ * Set the collation strength.
+ */
+PHP_FUNCTION( collator_set_strength )
+{
+ long strength;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, Collator_ce_ptr, &strength ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_set_strength: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ /* Set given strength. */
+ ucol_setStrength( co->ucoll, strength );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_attr.h b/ext/intl/collator/collator_attr.h
new file mode 100644
index 0000000..85636cc
--- /dev/null
+++ b/ext/intl/collator/collator_attr.h
@@ -0,0 +1,28 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_ATTR_H
+#define CCOLLATOR_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_attribute );
+PHP_FUNCTION( collator_set_attribute );
+PHP_FUNCTION( collator_get_strength );
+PHP_FUNCTION( collator_set_strength );
+
+#endif // COLLATOR_ATTR_H
diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c
new file mode 100644
index 0000000..d1fa10e
--- /dev/null
+++ b/ext/intl/collator/collator_class.c
@@ -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: 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;
+static zend_object_handlers Collator_handlers;
+
+/*
+ * 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 );
+ object_properties_init(&intern->zo, ce);
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ Collator_objects_dtor,
+ (zend_objects_free_object_storage_t)Collator_objects_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = &Collator_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.
+*/
+ZEND_BEGIN_ARG_INFO_EX( collator_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( collator_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+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()
+
+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
+ */
+
+zend_function_entry Collator_class_functions[] = {
+ PHP_ME( Collator, __construct, collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( collator_create ), collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( compare, ZEND_FN( collator_compare ), collator_2_args )
+ 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 )
+ PHP_NAMED_FE( getSortKey, ZEND_FN( collator_get_sort_key ), collator_2_args )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 );
+
+ memcpy(&Collator_handlers, zend_get_std_object_handlers(),
+ sizeof Collator_handlers);
+ /* Collator has no usable clone semantics - ucol_cloneBinary/ucol_openBinary require binary buffer
+ for which we don't have the place to keep */
+ Collator_handlers.clone_obj = NULL;
+
+ /* 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 100644
index 0000000..7a56dfc
--- /dev/null
+++ b/ext/intl/collator/collator_class.h
@@ -0,0 +1,69 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "../intl_data.h"
+
+#include <unicode/ucol.h>
+
+typedef struct {
+ zend_object zo;
+
+ // error handling
+ intl_error err;
+
+ // ICU collator
+ UCollator* ucoll;
+} 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 INTL_METHOD_FETCH_OBJECT(Collator, co)
+
+// 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 100644
index 0000000..4384558
--- /dev/null
+++ b/ext/intl/collator/collator_compare.c
@@ -0,0 +1,133 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_compare.h"
+#include "intl_convert.h"
+
+/* {{{ proto int Collator::compare( string $str1, string $str2 )
+ * Compare two strings. }}} */
+/* {{{ proto int collator_compare( Collator $coll, string $str1, string $str2 )
+ * Compare two strings.
+ */
+PHP_FUNCTION( collator_compare )
+{
+ char* str1 = NULL;
+ char* str2 = NULL;
+ int str1_len = 0;
+ int str2_len = 0;
+
+ UChar* ustr1 = NULL;
+ UChar* ustr2 = NULL;
+ int ustr1_len = 0;
+ int ustr2_len = 0;
+
+ UCollationResult result;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
+ &object, Collator_ce_ptr, &str1, &str1_len, &str2, &str2_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_compare: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ if (!co || !co->ucoll) {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Object not initialized", 0 TSRMLS_CC );
+ php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized");
+
+ RETURN_FALSE;
+ }
+
+ /*
+ * Compare given strings (converting them to UTF-16 first).
+ */
+
+ /* First convert the strings to UTF-16. */
+ intl_convert_utf8_to_utf16(
+ &ustr1, &ustr1_len, str1, str1_len, COLLATOR_ERROR_CODE_P( co ) );
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ /* Set global error code. */
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Error converting first argument to UTF-16", 0 TSRMLS_CC );
+ if (ustr1) {
+ efree( ustr1 );
+ }
+ RETURN_FALSE;
+ }
+
+ intl_convert_utf8_to_utf16(
+ &ustr2, &ustr2_len, str2, str2_len, COLLATOR_ERROR_CODE_P( co ) );
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ /* Set global error code. */
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Error converting second argument to UTF-16", 0 TSRMLS_CC );
+ if (ustr1) {
+ efree( ustr1 );
+ }
+ if (ustr2) {
+ efree( ustr2 );
+ }
+ RETURN_FALSE;
+ }
+
+ /* Then compare them. */
+ result = ucol_strcoll(
+ co->ucoll,
+ ustr1, ustr1_len,
+ ustr2, ustr2_len );
+
+ if( ustr1 )
+ efree( ustr1 );
+ if( ustr2 )
+ efree( ustr2 );
+
+ /* Return result of the comparison. */
+ RETURN_LONG( result );
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_compare.h b/ext/intl/collator/collator_compare.h
new file mode 100644
index 0000000..4e38b79
--- /dev/null
+++ b/ext/intl/collator/collator_compare.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_COMPARE_H
+#define COLLATOR_COMPARE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_compare );
+
+#endif // COLLATOR_COMPARE_H
diff --git a/ext/intl/collator/collator_convert.c b/ext/intl/collator/collator_convert.c
new file mode 100644
index 0000000..e989d4c
--- /dev/null
+++ b/ext/intl/collator/collator_convert.c
@@ -0,0 +1,485 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_is_numeric.h"
+#include "collator_convert.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+#include <php.h>
+
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1)
+#define CAST_OBJECT_SHOULD_FREE ,0
+#else
+#define CAST_OBJECT_SHOULD_FREE
+#endif
+
+#define COLLATOR_CONVERT_RETURN_FAILED(retval) { \
+ zval_add_ref( &retval ); \
+ return retval; \
+ }
+
+/* {{{ collator_convert_hash_item_from_utf8_to_utf16 */
+static void collator_convert_hash_item_from_utf8_to_utf16(
+ HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex,
+ UErrorCode* status )
+{
+ const char* old_val;
+ int old_val_len;
+ UChar* new_val = NULL;
+ int new_val_len = 0;
+ zval** hashData = NULL;
+ zval* znew_val = NULL;
+
+ /* Get current hash item. */
+ zend_hash_get_current_data( hash, (void**) &hashData );
+
+ /* Process string values only. */
+ if( Z_TYPE_P( *hashData ) != IS_STRING )
+ return;
+
+ old_val = Z_STRVAL_P( *hashData );
+ old_val_len = Z_STRLEN_P( *hashData );
+
+ /* Convert it from UTF-8 to UTF-16LE and save the result to new_val[_len]. */
+ intl_convert_utf8_to_utf16( &new_val, &new_val_len, old_val, old_val_len, status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ /* Update current hash item with the converted value. */
+ MAKE_STD_ZVAL( znew_val );
+ ZVAL_STRINGL( znew_val, (char*)new_val, UBYTES(new_val_len), FALSE );
+
+ if( hashKeyType == HASH_KEY_IS_STRING )
+ {
+ zend_hash_update( hash, hashKey, strlen( hashKey ) + 1,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+ else /* hashKeyType == HASH_KEY_IS_LONG */
+ {
+ zend_hash_index_update( hash, hashIndex,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_item_from_utf16_to_utf8 */
+static void collator_convert_hash_item_from_utf16_to_utf8(
+ HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex,
+ UErrorCode* status )
+{
+ const char* old_val;
+ int old_val_len;
+ char* new_val = NULL;
+ int new_val_len = 0;
+ zval** hashData = NULL;
+ zval* znew_val = NULL;
+
+ /* Get current hash item. */
+ zend_hash_get_current_data( hash, (void**) &hashData );
+
+ /* Process string values only. */
+ if( Z_TYPE_P( *hashData ) != IS_STRING )
+ return;
+
+ old_val = Z_STRVAL_P( *hashData );
+ old_val_len = Z_STRLEN_P( *hashData );
+
+ /* Convert it from UTF-16LE to UTF-8 and save the result to new_val[_len]. */
+ intl_convert_utf16_to_utf8( &new_val, &new_val_len,
+ (UChar*)old_val, UCHARS(old_val_len), status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ /* Update current hash item with the converted value. */
+ MAKE_STD_ZVAL( znew_val );
+ ZVAL_STRINGL( znew_val, (char*)new_val, new_val_len, FALSE );
+
+ if( hashKeyType == HASH_KEY_IS_STRING )
+ {
+ zend_hash_update( hash, hashKey, strlen( hashKey ) + 1,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+ else /* hashKeyType == HASH_KEY_IS_LONG */
+ {
+ zend_hash_index_update( hash, hashIndex,
+ (void*) &znew_val, sizeof(zval*), NULL );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_from_utf8_to_utf16
+ * Convert values of the given hash from UTF-8 encoding to UTF-16LE.
+ */
+void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status )
+{
+ ulong hashIndex = 0;
+ char* hashKey = NULL;
+ int hashKeyType = 0;
+
+ zend_hash_internal_pointer_reset( hash );
+ while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) )
+ != HASH_KEY_NON_EXISTANT )
+ {
+ /* Convert current hash item from UTF-8 to UTF-16LE. */
+ collator_convert_hash_item_from_utf8_to_utf16(
+ hash, hashKeyType, hashKey, hashIndex, status );
+ if( U_FAILURE( *status ) )
+ return;
+
+ /* Proceed to the next item. */
+ zend_hash_move_forward( hash );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_hash_from_utf16_to_utf8
+ * Convert values of the given hash from UTF-16LE encoding to UTF-8.
+ */
+void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status )
+{
+ ulong hashIndex = 0;
+ char* hashKey = NULL;
+ int hashKeyType = 0;
+
+ zend_hash_internal_pointer_reset( hash );
+ while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) )
+ != HASH_KEY_NON_EXISTANT )
+ {
+ /* Convert current hash item from UTF-16LE to UTF-8. */
+ collator_convert_hash_item_from_utf16_to_utf8(
+ hash, hashKeyType, hashKey, hashIndex, status );
+ if( U_FAILURE( *status ) ) {
+ return;
+ }
+
+ /* Proceed to the next item. */
+ zend_hash_move_forward( hash );
+ }
+}
+/* }}} */
+
+/* {{{ collator_convert_zstr_utf16_to_utf8
+ *
+ * Convert string from utf16 to utf8.
+ *
+ * @param zval* utf16_zval String to convert.
+ *
+ * @return zval* Converted string.
+ */
+zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval )
+{
+ zval* utf8_zval = NULL;
+ char* str = NULL;
+ int str_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ /* Convert to utf8 then. */
+ intl_convert_utf16_to_utf8( &str, &str_len,
+ (UChar*) Z_STRVAL_P(utf16_zval), UCHARS( Z_STRLEN_P(utf16_zval) ), &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error converting utf16 to utf8 in collator_convert_zval_utf16_to_utf8()" );
+
+ ALLOC_INIT_ZVAL( utf8_zval );
+ ZVAL_STRINGL( utf8_zval, str, str_len, FALSE );
+
+ return utf8_zval;
+}
+/* }}} */
+
+/* {{{ collator_convert_zstr_utf8_to_utf16
+ *
+ * Convert string from utf8 to utf16.
+ *
+ * @param zval* utf8_zval String to convert.
+ *
+ * @return zval* Converted string.
+ */
+zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval )
+{
+ zval* zstr = NULL;
+ UChar* ustr = NULL;
+ int ustr_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ /* Convert the string to UTF-16. */
+ intl_convert_utf8_to_utf16(
+ &ustr, &ustr_len,
+ Z_STRVAL_P( utf8_zval ), Z_STRLEN_P( utf8_zval ),
+ &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error casting object to string in collator_convert_zstr_utf8_to_utf16()" );
+
+ /* Set string. */
+ ALLOC_INIT_ZVAL( zstr );
+ ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE );
+
+ return zstr;
+}
+/* }}} */
+
+/* {{{ collator_convert_object_to_string
+ * Convert object to UTF16-encoded string.
+ */
+zval* collator_convert_object_to_string( zval* obj TSRMLS_DC )
+{
+ zval* zstr = NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ UChar* ustr = NULL;
+ int ustr_len = 0;
+
+ /* Bail out if it's not an object. */
+ if( Z_TYPE_P( obj ) != IS_OBJECT )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+
+ /* Try object's handlers. */
+ if( Z_OBJ_HT_P(obj)->get )
+ {
+ zstr = Z_OBJ_HT_P(obj)->get( obj TSRMLS_CC );
+
+ switch( Z_TYPE_P( zstr ) )
+ {
+ case IS_OBJECT:
+ {
+ /* Bail out. */
+ zval_ptr_dtor( &zstr );
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ } break;
+
+ case IS_STRING:
+ break;
+
+ default:
+ {
+ convert_to_string( zstr );
+ } break;
+ }
+ }
+ else if( Z_OBJ_HT_P(obj)->cast_object )
+ {
+ ALLOC_INIT_ZVAL( zstr );
+
+ if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING CAST_OBJECT_SHOULD_FREE TSRMLS_CC ) == FAILURE )
+ {
+ /* cast_object failed => bail out. */
+ zval_ptr_dtor( &zstr );
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+ }
+
+ /* Object wasn't successfuly converted => bail out. */
+ if( zstr == NULL )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( obj );
+ }
+
+ /* Convert the string to UTF-16. */
+ intl_convert_utf8_to_utf16(
+ &ustr, &ustr_len,
+ Z_STRVAL_P( zstr ), Z_STRLEN_P( zstr ),
+ &status );
+ if( U_FAILURE( status ) )
+ php_error( E_WARNING, "Error casting object to string in collator_convert_object_to_string()" );
+
+ /* Cleanup zstr to hold utf16 string. */
+ zval_dtor( zstr );
+
+ /* Set string. */
+ ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE );
+
+ /* Don't free ustr cause it's set in zstr without copy.
+ * efree( ustr );
+ */
+
+ return zstr;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_number
+ *
+ * Convert string to number.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number. If str is not numeric string return number zero.
+ */
+zval* collator_convert_string_to_number( zval* str )
+{
+ zval* num = collator_convert_string_to_number_if_possible( str );
+ if( num == str )
+ {
+ /* String wasn't converted => return zero. */
+ zval_ptr_dtor( &num );
+
+ ALLOC_INIT_ZVAL( num );
+ ZVAL_LONG( num, 0 );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_double
+ *
+ * Convert string to double.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number. If str is not numeric string return number zero.
+ */
+zval* collator_convert_string_to_double( zval* str )
+{
+ zval* num = collator_convert_string_to_number( str );
+ if( Z_TYPE_P(num) == IS_LONG )
+ {
+ ZVAL_DOUBLE( num, Z_LVAL_P( num ) );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_convert_string_to_number_if_possible
+ *
+ * Convert string to numer.
+ *
+ * @param zval* str String to convert.
+ *
+ * @return zval* Number if str is numeric string. Otherwise
+ * original str param.
+ */
+zval* collator_convert_string_to_number_if_possible( zval* str )
+{
+ zval* num = NULL;
+ int is_numeric = 0;
+ long lval = 0;
+ double dval = 0;
+
+ if( Z_TYPE_P( str ) != IS_STRING )
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( str );
+ }
+
+ if( ( is_numeric = collator_is_numeric( (UChar*) Z_STRVAL_P(str), UCHARS( Z_STRLEN_P(str) ), &lval, &dval, 1 ) ) )
+ {
+ ALLOC_INIT_ZVAL( num );
+
+ if( is_numeric == IS_LONG )
+ Z_LVAL_P(num) = lval;
+ if( is_numeric == IS_DOUBLE )
+ Z_DVAL_P(num) = dval;
+
+ Z_TYPE_P(num) = is_numeric;
+ }
+ else
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( str );
+ }
+
+ return num;
+}
+/* }}} */
+
+/* {{{ collator_make_printable_zval
+ *
+ * Returns string from input zval.
+ *
+ * @param zval* arg zval to get string from
+ *
+ * @return zval* UTF16 string.
+ */
+zval* collator_make_printable_zval( zval* arg )
+{
+ zval arg_copy;
+ int use_copy = 0;
+ zval* str = NULL;
+
+ if( Z_TYPE_P(arg) != IS_STRING )
+ {
+ zend_make_printable_zval(arg, &arg_copy, &use_copy);
+
+ if( use_copy )
+ {
+ str = collator_convert_zstr_utf8_to_utf16( &arg_copy );
+ zval_dtor( &arg_copy );
+ }
+ else
+ {
+ str = collator_convert_zstr_utf8_to_utf16( arg );
+ }
+ }
+ else
+ {
+ COLLATOR_CONVERT_RETURN_FAILED( arg );
+ }
+
+ return str;
+}
+/* }}} */
+
+/* {{{ collator_normalize_sort_argument
+ *
+ * Normalize argument to use in sort's compare function.
+ *
+ * @param zval* arg Sort's argument to normalize.
+ *
+ * @return zval* Normalized copy of arg or unmodified arg
+ * if normalization is not needed.
+ */
+zval* collator_normalize_sort_argument( zval* arg )
+{
+ zval* n_arg = NULL;
+
+ if( Z_TYPE_P( arg ) != IS_STRING )
+ {
+ /* If its not a string then nothing to do.
+ * Return original arg.
+ */
+ COLLATOR_CONVERT_RETURN_FAILED( arg );
+ }
+
+ /* Try convert to number. */
+ n_arg = collator_convert_string_to_number_if_possible( arg );
+
+ if( n_arg == arg )
+ {
+ /* Conversion to number failed. */
+ zval_ptr_dtor( &n_arg );
+
+ /* Convert string to utf8. */
+ n_arg = collator_convert_zstr_utf16_to_utf8( arg );
+ }
+
+ return n_arg;
+}
+/* }}} */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_convert.h b/ext/intl/collator/collator_convert.h
new file mode 100644
index 0000000..8322ea9
--- /dev/null
+++ b/ext/intl/collator/collator_convert.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_CONVERT_H
+#define COLLATOR_CONVERT_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+
+void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status );
+void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status );
+
+zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval );
+zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval );
+
+zval* collator_normalize_sort_argument( zval* arg );
+zval* collator_convert_object_to_string( zval* obj TSRMLS_DC );
+zval* collator_convert_string_to_number( zval* arg );
+zval* collator_convert_string_to_number_if_possible( zval* str );
+zval* collator_convert_string_to_double( zval* str );
+
+zval* collator_make_printable_zval( zval* arg );
+
+#endif // COLLATOR_CONVERT_H
diff --git a/ext/intl/collator/collator_create.c b/ext/intl/collator/collator_create.c
new file mode 100644
index 0000000..b2a9968
--- /dev/null
+++ b/ext/intl/collator/collator_create.c
@@ -0,0 +1,87 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_create.h"
+#include "intl_data.h"
+
+/* {{{ */
+static void collator_ctor(INTERNAL_FUNCTION_PARAMETERS)
+{
+ char* locale;
+ int locale_len = 0;
+ zval* object;
+ Collator_object* co;
+
+ intl_error_reset( NULL TSRMLS_CC );
+ object = return_value;
+ /* 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 );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value);
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ /* Open ICU collator. */
+ co->ucoll = ucol_open( locale, COLLATOR_ERROR_CODE_P( co ) );
+ INTL_CTOR_CHECK_STATUS(co, "collator_create: unable to open ICU collator");
+}
+/* }}} */
+
+/* {{{ proto Collator collator_create( string $locale )
+ * Create collator.
+ */
+PHP_FUNCTION( collator_create )
+{
+ object_init_ex( return_value, Collator_ce_ptr );
+ collator_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto Collator Collator::__construct( string $locale )
+ * Collator object constructor.
+ */
+PHP_METHOD( Collator, __construct )
+{
+ return_value = getThis();
+ collator_ctor(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_create.h b/ext/intl/collator/collator_create.h
new file mode 100644
index 0000000..b740e82
--- /dev/null
+++ b/ext/intl/collator/collator_create.h
@@ -0,0 +1,27 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_CREATE_H
+#define COLLATOR_CREATE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_create );
+
+PHP_METHOD( Collator, __construct );
+
+#endif // COLLATOR_CREATE_H
diff --git a/ext/intl/collator/collator_error.c b/ext/intl/collator/collator_error.c
new file mode 100644
index 0000000..c4e4125
--- /dev/null
+++ b/ext/intl/collator/collator_error.c
@@ -0,0 +1,94 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator_class.h"
+#include "collator_error.h"
+
+/* {{{ proto int Collator::getErrorCode( Collator $coll )
+ * Get collator's last error code. }}} */
+/* {{{ proto int collator_get_error_code( Collator $coll )
+ * Get collator's last error code.
+ */
+PHP_FUNCTION( collator_get_error_code )
+{
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object (without resetting its last error code). */
+ co = (Collator_object *) zend_object_store_get_object(object TSRMLS_CC);
+ if( co == NULL )
+ RETURN_FALSE;
+
+ /* Return collator's last error code. */
+ RETURN_LONG( COLLATOR_ERROR_CODE( co ) );
+}
+/* }}} */
+
+/* {{{ proto string Collator::getErrorMessage( Collator $coll )
+ * Get text description for collator's last error code. }}} */
+/* {{{ proto string collator_get_error_message( Collator $coll )
+ * Get text description for collator's last error code.
+ */
+PHP_FUNCTION( collator_get_error_message )
+{
+ const char* message = NULL;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Collator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object (without resetting its last error code). */
+ co = (Collator_object *) zend_object_store_get_object( object TSRMLS_CC );
+ if( co == NULL )
+ RETURN_FALSE;
+
+ /* Return last error message. */
+ message = intl_error_get_message( COLLATOR_ERROR_P( co ) TSRMLS_CC );
+ RETURN_STRING( (char*)message, FALSE );
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_error.h b/ext/intl/collator/collator_error.h
new file mode 100644
index 0000000..b2f44ea
--- /dev/null
+++ b/ext/intl/collator/collator_error.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_ERROR_H
+#define COLLATOR_ERROR_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_error_code );
+PHP_FUNCTION( collator_get_error_message );
+
+#endif // COLLATOR_ERROR_H
diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c
new file mode 100644
index 0000000..a8abfac
--- /dev/null
+++ b/ext/intl/collator/collator_is_numeric.c
@@ -0,0 +1,305 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "collator_is_numeric.h"
+
+#if ZEND_MODULE_API_NO < 20071006
+/* not 5.3 */
+#ifndef ALLOCA_FLAG
+#define ALLOCA_FLAG(use_heap)
+#endif
+#define _do_alloca(x, y) do_alloca((x))
+#define _free_alloca(x, y) free_alloca((x))
+#else
+#define _do_alloca do_alloca
+#define _free_alloca free_alloca
+#endif
+/* {{{ collator_u_strtod
+ * Taken from PHP6:zend_u_strtod()
+ */
+static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
+{
+ const UChar *u = nptr, *nstart;
+ UChar c = *u;
+ int any = 0;
+ ALLOCA_FLAG(use_heap);
+
+ while (u_isspace(c)) {
+ c = *++u;
+ }
+ nstart = u;
+
+ if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
+ c = *++u;
+ }
+
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any = 1;
+ c = *++u;
+ }
+
+ if (c == 0x2E /*'.'*/) {
+ c = *++u;
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any = 1;
+ c = *++u;
+ }
+ }
+
+ if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) {
+ const UChar *e = u;
+ int any_exp = 0;
+
+ c = *++u;
+ if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) {
+ c = *++u;
+ }
+
+ while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) {
+ any_exp = 1;
+ c = *++u;
+ }
+
+ if (!any_exp) {
+ u = e;
+ }
+ }
+
+ if (any) {
+ char buf[64], *numbuf, *bufpos;
+ int length = u - nstart;
+ double value;
+
+ if (length < sizeof(buf)) {
+ numbuf = buf;
+ } else {
+ numbuf = (char *) _do_alloca(length + 1, use_heap);
+ }
+
+ bufpos = numbuf;
+
+ while (nstart < u) {
+ *bufpos++ = (char) *nstart++;
+ }
+
+ *bufpos = '\0';
+ value = zend_strtod(numbuf, NULL);
+
+ if (numbuf != buf) {
+ _free_alloca(numbuf, use_heap);
+ }
+
+ if (endptr != NULL) {
+ *endptr = (UChar *)u;
+ }
+
+ return value;
+ }
+
+ if (endptr != NULL) {
+ *endptr = (UChar *)nptr;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ collator_u_strtol
+ * Taken from PHP6:zend_u_strtol()
+ *
+ * Convert a Unicode string to a long integer.
+ *
+ * Ignores `locale' stuff.
+ */
+static long collator_u_strtol(nptr, endptr, base)
+ const UChar *nptr;
+ UChar **endptr;
+ register int base;
+{
+ register const UChar *s = nptr;
+ register unsigned long acc;
+ register UChar c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ if (s == NULL) {
+ errno = ERANGE;
+ if (endptr != NULL) {
+ *endptr = NULL;
+ }
+ return 0;
+ }
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (u_isspace(c));
+ if (c == 0x2D /*'-'*/) {
+ neg = 1;
+ c = *s++;
+ } else if (c == 0x2B /*'+'*/)
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ (c == 0x30 /*'0'*/)
+ && (*s == 0x78 /*'x'*/ || *s == 0x58 /*'X'*/)) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = (c == 0x30 /*'0'*/) ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/)
+ c -= 0x30 /*'0'*/;
+ else if (c >= 0x41 /*'A'*/ && c <= 0x5A /*'Z'*/)
+ c -= 0x41 /*'A'*/ - 10;
+ else if (c >= 0x61 /*'a'*/ && c <= 0x7A /*'z'*/)
+ c -= 0x61 /*'a'*/ - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (UChar *)(any ? s - 1 : nptr);
+ return (acc);
+}
+/* }}} */
+
+
+/* {{{ collator_is_numeric]
+ * Taken from PHP6:is_numeric_unicode()
+ */
+zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors )
+{
+ long local_lval;
+ double local_dval;
+ UChar *end_ptr_long, *end_ptr_double;
+ int conv_base=10;
+
+ if (!length) {
+ return 0;
+ }
+
+ /* handle hex numbers */
+ if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) {
+ conv_base=16;
+ }
+
+ errno=0;
+ local_lval = collator_u_strtol(str, &end_ptr_long, conv_base);
+ if (errno != ERANGE) {
+ if (end_ptr_long == str+length) { /* integer string */
+ if (lval) {
+ *lval = local_lval;
+ }
+ return IS_LONG;
+ } else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */
+ return 0;
+ }
+ } else {
+ end_ptr_long = NULL;
+ }
+
+ if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */
+ /* UTODO: keep compatibility with is_numeric_string() here? */
+ return 0;
+ }
+
+ local_dval = collator_u_strtod(str, &end_ptr_double);
+ if (local_dval == 0 && end_ptr_double == str) {
+ end_ptr_double = NULL;
+ } else {
+ if (end_ptr_double == str+length) { /* floating point string */
+ if (!zend_finite(local_dval)) {
+ /* "inf","nan" and maybe other weird ones */
+ return 0;
+ }
+
+ if (dval) {
+ *dval = local_dval;
+ }
+ return IS_DOUBLE;
+ }
+ }
+
+ if (!allow_errors) {
+ return 0;
+ }
+ if (allow_errors == -1) {
+ zend_error(E_NOTICE, "A non well formed numeric value encountered");
+ }
+
+ if (allow_errors) {
+ if (end_ptr_double > end_ptr_long && dval) {
+ *dval = local_dval;
+ return IS_DOUBLE;
+ } else if (end_ptr_long && lval) {
+ *lval = local_lval;
+ return IS_LONG;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_is_numeric.h b/ext/intl/collator/collator_is_numeric.h
new file mode 100644
index 0000000..585d589
--- /dev/null
+++ b/ext/intl/collator/collator_is_numeric.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_IS_NUMERIC_H
+#define COLLATOR_IS_NUMERIC_H
+
+#include <php.h>
+#include <unicode/uchar.h>
+
+zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors );
+
+#endif // COLLATOR_IS_NUMERIC_H
diff --git a/ext/intl/collator/collator_locale.c b/ext/intl/collator/collator_locale.c
new file mode 100644
index 0000000..b30b021
--- /dev/null
+++ b/ext/intl/collator/collator_locale.c
@@ -0,0 +1,80 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 )
+{
+ long 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;
+
+ if (!co || !co->ucoll) {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Object not initialized", 0 TSRMLS_CC );
+ php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized");
+
+ RETURN_FALSE;
+ }
+
+ /* Get locale by specified type. */
+ locale_name = (char*) ucol_getLocaleByType(
+ co->ucoll, type, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error getting locale by type" );
+
+ /* Return it. */
+ RETVAL_STRINGL( locale_name, strlen(locale_name), TRUE );
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/collator/collator_locale.h b/ext/intl/collator/collator_locale.h
new file mode 100644
index 0000000..bda90cd
--- /dev/null
+++ b/ext/intl/collator/collator_locale.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef COLLATOR_LOCALE_H
+#define COLLATOR_LOCALE_H
+
+#include <php.h>
+
+PHP_FUNCTION( collator_get_locale );
+
+#endif // COLLATOR_LOCALE_H
diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c
new file mode 100644
index 0000000..0785111
--- /dev/null
+++ b/ext/intl/collator/collator_sort.c
@@ -0,0 +1,621 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "collator.h"
+#include "collator_class.h"
+#include "collator_sort.h"
+#include "collator_convert.h"
+#include "intl_convert.h"
+
+#if !defined(HAVE_PTRDIFF_T) && !defined(_PTRDIFF_T_DEFINED)
+typedef long ptrdiff_t;
+#endif
+
+/**
+ * Declare 'index' which will point to sort key in sort key
+ * buffer.
+ */
+typedef struct _collator_sort_key_index {
+ char* key; /* pointer to sort key */
+ zval** zstr; /* pointer to original string(hash-item) */
+} collator_sort_key_index_t;
+
+ZEND_EXTERN_MODULE_GLOBALS( intl )
+
+static const size_t DEF_SORT_KEYS_BUF_SIZE = 1048576;
+static const size_t DEF_SORT_KEYS_BUF_INCREMENT = 1048576;
+
+static const size_t DEF_SORT_KEYS_INDX_BUF_SIZE = 1048576;
+static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576;
+
+static const size_t DEF_UTF16_BUF_SIZE = 1024;
+
+/* {{{ collator_regular_compare_function */
+static int collator_regular_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ Collator_object* co = NULL;
+
+ int rc = SUCCESS;
+
+ zval* str1 = collator_convert_object_to_string( op1 TSRMLS_CC );
+ zval* str2 = collator_convert_object_to_string( op2 TSRMLS_CC );
+
+ zval* num1 = NULL;
+ zval* num2 = NULL;
+ zval* norm1 = NULL;
+ zval* norm2 = NULL;
+
+ /* If both args are strings AND either of args is not numeric string
+ * then use ICU-compare. Otherwise PHP-compare. */
+ if( Z_TYPE_P(str1) == IS_STRING && Z_TYPE_P(str2) == IS_STRING &&
+ ( str1 == ( num1 = collator_convert_string_to_number_if_possible( str1 ) ) ||
+ str2 == ( num2 = collator_convert_string_to_number_if_possible( str2 ) ) ) )
+ {
+ /* Fetch collator object. */
+ co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC );
+
+ if (!co || !co->ucoll) {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Object not initialized", 0 TSRMLS_CC );
+ php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized");
+ }
+
+ /* Compare the strings using ICU. */
+ result->value.lval = ucol_strcoll(
+ co->ucoll,
+ INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1),
+ INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) );
+ result->type = IS_LONG;
+ }
+ else
+ {
+ /* num1 is set if str1 and str2 are strings. */
+ if( num1 )
+ {
+ if( num1 == str1 )
+ {
+ /* str1 is string but not numeric string
+ * just convert it to utf8.
+ */
+ norm1 = collator_convert_zstr_utf16_to_utf8( str1 );
+
+ /* num2 is not set but str2 is string => do normalization. */
+ norm2 = collator_normalize_sort_argument( str2 );
+ }
+ else
+ {
+ /* str1 is numeric strings => passthru to PHP-compare. */
+ zval_add_ref( &num1 );
+ norm1 = num1;
+
+ /* str2 is numeric strings => passthru to PHP-compare. */
+ zval_add_ref( &num2 );
+ norm2 = num2;
+ }
+ }
+ else
+ {
+ /* num1 is not set if str1 or str2 is not a string => do normalization. */
+ norm1 = collator_normalize_sort_argument( str1 );
+
+ /* if num1 is not set then num2 is not set as well => do normalization. */
+ norm2 = collator_normalize_sort_argument( str2 );
+ }
+
+ rc = compare_function( result, norm1, norm2 TSRMLS_CC );
+
+ zval_ptr_dtor( &norm1 );
+ zval_ptr_dtor( &norm2 );
+ }
+
+ if( num1 )
+ zval_ptr_dtor( &num1 );
+
+ if( num2 )
+ zval_ptr_dtor( &num2 );
+
+ zval_ptr_dtor( &str1 );
+ zval_ptr_dtor( &str2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_numeric_compare_function
+ * Convert input args to double and compare it.
+ */
+static int collator_numeric_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ int rc = SUCCESS;
+ zval* num1 = NULL;
+ zval* num2 = NULL;
+
+ if( Z_TYPE_P(op1) == IS_STRING )
+ {
+ num1 = collator_convert_string_to_double( op1 );
+ op1 = num1;
+ }
+
+ if( Z_TYPE_P(op2) == IS_STRING )
+ {
+ num2 = collator_convert_string_to_double( op2 );
+ op2 = num2;
+ }
+
+ rc = numeric_compare_function( result, op1, op2 TSRMLS_CC);
+
+ if( num1 )
+ zval_ptr_dtor( &num1 );
+ if( num2 )
+ zval_ptr_dtor( &num2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_icu_compare_function
+ * Direct use of ucol_strcoll.
+*/
+static int collator_icu_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC)
+{
+ int rc = SUCCESS;
+ Collator_object* co = NULL;
+ zval* str1 = NULL;
+ zval* str2 = NULL;
+
+ str1 = collator_make_printable_zval( op1 );
+ str2 = collator_make_printable_zval( op2 );
+
+ /* Fetch collator object. */
+ co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC );
+
+ /* Compare the strings using ICU. */
+ result->value.lval = ucol_strcoll(
+ co->ucoll,
+ INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1),
+ INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) );
+ result->type = IS_LONG;
+
+ zval_ptr_dtor( &str1 );
+ zval_ptr_dtor( &str2 );
+
+ return rc;
+}
+/* }}} */
+
+/* {{{ collator_compare_func
+ * Taken from PHP5 source (array_data_compare).
+ */
+static int collator_compare_func( const void* a, const void* b TSRMLS_DC )
+{
+ Bucket *f;
+ Bucket *s;
+ zval result;
+ zval *first;
+ zval *second;
+
+ f = *((Bucket **) a);
+ s = *((Bucket **) b);
+
+ first = *((zval **) f->pData);
+ second = *((zval **) s->pData);
+
+ if( INTL_G(compare_func)( &result, first, second TSRMLS_CC) == FAILURE )
+ return 0;
+
+ if( Z_TYPE(result) == IS_DOUBLE )
+ {
+ if( Z_DVAL(result) < 0 )
+ return -1;
+ else if( Z_DVAL(result) > 0 )
+ return 1;
+ else
+ return 0;
+ }
+
+ convert_to_long(&result);
+
+ if( Z_LVAL(result) < 0 )
+ return -1;
+ else if( Z_LVAL(result) > 0 )
+ return 1;
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ collator_cmp_sort_keys
+ * Compare sort keys
+ */
+static int collator_cmp_sort_keys( const void *p1, const void *p2 TSRMLS_DC )
+{
+ char* key1 = ((collator_sort_key_index_t*)p1)->key;
+ char* key2 = ((collator_sort_key_index_t*)p2)->key;
+
+ return strcmp( key1, key2 );
+}
+/* }}} */
+
+/* {{{ collator_get_compare_function
+ * Choose compare function according to sort flags.
+ */
+static collator_compare_func_t collator_get_compare_function( const long sort_flags )
+{
+ collator_compare_func_t func;
+
+ switch( sort_flags )
+ {
+ case COLLATOR_SORT_NUMERIC:
+ func = collator_numeric_compare_function;
+ break;
+
+ case COLLATOR_SORT_STRING:
+ func = collator_icu_compare_function;
+ break;
+
+ case COLLATOR_SORT_REGULAR:
+ default:
+ func = collator_regular_compare_function;
+ break;
+ }
+
+ return func;
+}
+/* }}} */
+
+/* {{{ collator_sort_internal
+ * Common code shared by collator_sort() and collator_asort() API functions.
+ */
+static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS )
+{
+ zval* array = NULL;
+ HashTable* hash = NULL;
+ zval* saved_collator = NULL;
+ long sort_flags = COLLATOR_SORT_REGULAR;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa|l",
+ &object, Collator_ce_ptr, &array, &sort_flags ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_sort_internal: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ /* Set 'compare function' according to sort flags. */
+ INTL_G(compare_func) = collator_get_compare_function( sort_flags );
+
+ hash = HASH_OF( array );
+
+ /* Convert strings in the specified array from UTF-8 to UTF-16. */
+ collator_convert_hash_from_utf8_to_utf16( hash, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" );
+
+ /* Save specified collator in the request-global (?) variable. */
+ saved_collator = INTL_G( current_collator );
+ INTL_G( current_collator ) = object;
+
+ /* Sort specified array. */
+ zend_hash_sort( hash, zend_qsort, collator_compare_func, renumber TSRMLS_CC );
+
+ /* Restore saved collator. */
+ INTL_G( current_collator ) = saved_collator;
+
+ /* Convert strings in the specified array back to UTF-8. */
+ collator_convert_hash_from_utf16_to_utf8( hash, COLLATOR_ERROR_CODE_P( co ) );
+ COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-16 to UTF-8" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool Collator::sort( Collator $coll, array(string) $arr [, int $sort_flags] )
+ * Sort array using specified collator. }}} */
+/* {{{ proto bool collator_sort( Collator $coll, array(string) $arr [, int $sort_flags] )
+ * Sort array using specified collator.
+ */
+PHP_FUNCTION( collator_sort )
+{
+ collator_sort_internal( TRUE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::sortWithSortKeys( Collator $coll, array(string) $arr )
+ * Equivalent to standard PHP sort using Collator.
+ * Uses ICU ucol_getSortKey for performance. }}} */
+/* {{{ proto bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr )
+ * Equivalent to standard PHP sort using Collator.
+ * Uses ICU ucol_getSortKey for performance.
+ */
+PHP_FUNCTION( collator_sort_with_sort_keys )
+{
+ zval* array = NULL;
+ HashTable* hash = NULL;
+ zval** hashData = NULL; /* currently processed item of input hash */
+
+ char* sortKeyBuf = NULL; /* buffer to store sort keys */
+ uint32_t sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; /* buffer size */
+ ptrdiff_t sortKeyBufOffset = 0; /* pos in buffer to store sort key */
+ int32_t sortKeyLen = 0; /* the length of currently processing key */
+ uint32_t bufLeft = 0;
+ uint32_t bufIncrement = 0;
+
+ collator_sort_key_index_t* sortKeyIndxBuf = NULL; /* buffer to store 'indexes' which will be passed to 'qsort' */
+ uint32_t sortKeyIndxBufSize = DEF_SORT_KEYS_INDX_BUF_SIZE;
+ uint32_t sortKeyIndxSize = sizeof( collator_sort_key_index_t );
+
+ uint32_t sortKeyCount = 0;
+ uint32_t j = 0;
+
+ UChar* utf16_buf = NULL; /* tmp buffer to hold current processing string in utf-16 */
+ int utf16_buf_size = DEF_UTF16_BUF_SIZE; /* the length of utf16_buf */
+ int utf16_len = 0; /* length of converted string */
+
+ HashTable* sortedHash = NULL;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+ &object, Collator_ce_ptr, &array ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_sort_with_sort_keys: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ if (!co || !co->ucoll) {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Object not initialized", 0 TSRMLS_CC );
+ php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized");
+
+ RETURN_FALSE;
+ }
+
+ /*
+ * Sort specified array.
+ */
+ hash = HASH_OF( array );
+
+ if( !hash || zend_hash_num_elements( hash ) == 0 )
+ RETURN_TRUE;
+
+ /* Create bufers */
+ sortKeyBuf = ecalloc( sortKeyBufSize, sizeof( char ) );
+ sortKeyIndxBuf = ecalloc( sortKeyIndxBufSize, sizeof( uint8_t ) );
+ utf16_buf = eumalloc( utf16_buf_size );
+
+ /* Iterate through input hash and create a sort key for each value. */
+ zend_hash_internal_pointer_reset( hash );
+ while( zend_hash_get_current_data( hash, (void**) &hashData ) == SUCCESS )
+ {
+ /* Convert current hash item from UTF-8 to UTF-16LE and save the result to utf16_buf. */
+
+ utf16_len = utf16_buf_size;
+
+ /* Process string values only. */
+ if( Z_TYPE_PP( hashData ) == IS_STRING )
+ {
+ intl_convert_utf8_to_utf16( &utf16_buf, &utf16_len, Z_STRVAL_PP( hashData ), Z_STRLEN_PP( hashData ), COLLATOR_ERROR_CODE_P( co ) );
+
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Sort with sort keys failed", 0 TSRMLS_CC );
+
+ if( utf16_buf )
+ efree( utf16_buf );
+
+ efree( sortKeyIndxBuf );
+ efree( sortKeyBuf );
+
+ RETURN_FALSE;
+ }
+ }
+ else
+ {
+ /* Set empty string */
+ utf16_len = 0;
+ utf16_buf[utf16_len] = 0;
+ }
+
+ if( (utf16_len + 1) > utf16_buf_size )
+ utf16_buf_size = utf16_len + 1;
+
+ /* Get sort key, reallocating the buffer if needed. */
+ bufLeft = sortKeyBufSize - sortKeyBufOffset;
+
+ sortKeyLen = ucol_getSortKey( co->ucoll,
+ utf16_buf,
+ utf16_len,
+ (uint8_t*)sortKeyBuf + sortKeyBufOffset,
+ bufLeft );
+
+ /* check for sortKeyBuf overflow, increasing its size of the buffer if needed */
+ if( sortKeyLen > bufLeft )
+ {
+ bufIncrement = ( sortKeyLen > DEF_SORT_KEYS_BUF_INCREMENT ) ? sortKeyLen : DEF_SORT_KEYS_BUF_INCREMENT;
+
+ sortKeyBufSize += bufIncrement;
+ bufLeft += bufIncrement;
+
+ sortKeyBuf = erealloc( sortKeyBuf, sortKeyBufSize );
+
+ sortKeyLen = ucol_getSortKey( co->ucoll, utf16_buf, utf16_len, (uint8_t*)sortKeyBuf + sortKeyBufOffset, bufLeft );
+ }
+
+ /* check sortKeyIndxBuf overflow, increasing its size of the buffer if needed */
+ if( ( sortKeyCount + 1 ) * sortKeyIndxSize > sortKeyIndxBufSize )
+ {
+ bufIncrement = ( sortKeyIndxSize > DEF_SORT_KEYS_INDX_BUF_INCREMENT ) ? sortKeyIndxSize : DEF_SORT_KEYS_INDX_BUF_INCREMENT;
+
+ sortKeyIndxBufSize += bufIncrement;
+
+ sortKeyIndxBuf = erealloc( sortKeyIndxBuf, sortKeyIndxBufSize );
+ }
+
+ sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset; /* remeber just offset, cause address */
+ /* of 'sortKeyBuf' may be changed due to realloc. */
+ sortKeyIndxBuf[sortKeyCount].zstr = hashData;
+
+ sortKeyBufOffset += sortKeyLen;
+ ++sortKeyCount;
+
+ zend_hash_move_forward( hash );
+ }
+
+ /* update ptrs to point to valid keys. */
+ for( j = 0; j < sortKeyCount; j++ )
+ sortKeyIndxBuf[j].key = sortKeyBuf + (ptrdiff_t)sortKeyIndxBuf[j].key;
+
+ /* sort it */
+ zend_qsort( sortKeyIndxBuf, sortKeyCount, sortKeyIndxSize, collator_cmp_sort_keys TSRMLS_CC );
+
+ /* for resulting hash we'll assign new hash keys rather then reordering */
+ ALLOC_HASHTABLE( sortedHash );
+ zend_hash_init( sortedHash, 0, NULL, ZVAL_PTR_DTOR, 0 );
+
+ for( j = 0; j < sortKeyCount; j++ )
+ {
+ zval_add_ref( sortKeyIndxBuf[j].zstr );
+ zend_hash_next_index_insert( sortedHash, sortKeyIndxBuf[j].zstr, sizeof(zval **), NULL );
+ }
+
+ /* Save sorted hash into return variable. */
+ zval_dtor( array );
+ (array)->value.ht = sortedHash;
+ (array)->type = IS_ARRAY;
+
+ if( utf16_buf )
+ efree( utf16_buf );
+
+ efree( sortKeyIndxBuf );
+ efree( sortKeyBuf );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool Collator::asort( Collator $coll, array(string) $arr )
+ * Sort array using specified collator, maintaining index association. }}} */
+/* {{{ proto bool collator_asort( Collator $coll, array(string) $arr )
+ * Sort array using specified collator, maintaining index association.
+ */
+PHP_FUNCTION( collator_asort )
+{
+ collator_sort_internal( FALSE, INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{ proto bool Collator::getSortKey( Collator $coll, string $str )
+ * Get a sort key for a string from a Collator. }}} */
+/* {{{ proto bool collator_get_sort_key( Collator $coll, string $str )
+ * Get a sort key for a string from a Collator. }}} */
+PHP_FUNCTION( collator_get_sort_key )
+{
+ char* str = NULL;
+ int str_len = 0;
+ UChar* ustr = NULL;
+ int ustr_len = 0;
+ uint8_t* key = NULL;
+ int key_len = 0;
+
+ COLLATOR_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, Collator_ce_ptr, &str, &str_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "collator_get_sort_key: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ COLLATOR_METHOD_FETCH_OBJECT;
+
+ if (!co || !co->ucoll) {
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Object not initialized", 0 TSRMLS_CC );
+ php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized");
+
+ RETURN_FALSE;
+ }
+
+ /*
+ * Compare given strings (converting them to UTF-16 first).
+ */
+
+ /* First convert the strings to UTF-16. */
+ intl_convert_utf8_to_utf16(
+ &ustr, &ustr_len, str, str_len, COLLATOR_ERROR_CODE_P( co ) );
+ if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) )
+ {
+ /* Set global error code. */
+ intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ),
+ "Error converting first argument to UTF-16", 0 TSRMLS_CC );
+ efree( ustr );
+ RETURN_FALSE;
+ }
+
+ /* ucol_getSortKey is exception in that the key length includes the
+ * NUL terminator*/
+ key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, key, 0);
+ if(!key_len) {
+ efree( ustr );
+ RETURN_FALSE;
+ }
+ key = emalloc(key_len);
+ key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, key, key_len);
+ efree( ustr );
+ if(!key_len) {
+ RETURN_FALSE;
+ }
+ RETURN_STRINGL((char *)key, key_len - 1, 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_sort.h b/ext/intl/collator/collator_sort.h
new file mode 100644
index 0000000..a990cdf
--- /dev/null
+++ b/ext/intl/collator/collator_sort.h
@@ -0,0 +1,30 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef 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_get_sort_key );
+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 100644
index 0000000..2821722
--- /dev/null
+++ b/ext/intl/common/common_error.c
@@ -0,0 +1,269 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 occurred 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 occurred 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 */
+ 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 );
+#if U_ICU_VERSION_MAJOR_NUM > 3 || U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM >= 6
+ INTL_EXPOSE_CONST( U_IDNA_ZERO_LENGTH_LABEL_ERROR );
+#endif
+#if U_ICU_VERSION_MAJOR_NUM > 3 || U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM >= 8
+ INTL_EXPOSE_CONST( U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR );
+#endif
+ INTL_EXPOSE_CONST( U_IDNA_ERROR_LIMIT );
+
+ /* Aliases for StringPrep */
+ INTL_EXPOSE_CONST( U_STRINGPREP_PROHIBITED_ERROR );
+ 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 100644
index 0000000..8716222
--- /dev/null
+++ b/ext/intl/common/common_error.h
@@ -0,0 +1,30 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_COMMON_ERROR_H
+#define INTL_COMMON_ERROR_H
+
+#include <php.h>
+
+PHP_FUNCTION( intl_get_error_code );
+PHP_FUNCTION( intl_get_error_message );
+PHP_FUNCTION( intl_is_failure );
+PHP_FUNCTION( intl_error_name );
+
+void intl_expose_icu_error_codes( INIT_FUNC_ARGS );
+
+#endif // INTL_COMMON_ERROR_H
diff --git a/ext/intl/config.m4 b/ext/intl/config.m4
new file mode 100644
index 0000000..0477c7f
--- /dev/null
+++ b/ext/intl/config.m4
@@ -0,0 +1,82 @@
+dnl config.m4 for extension intl
+
+dnl ##########################################################################
+dnl Initialize the extension
+PHP_ARG_ENABLE(intl, whether to enable internationalization support,
+[ --enable-intl Enable internationalization support])
+
+if test "$PHP_INTL" != "no"; then
+ PHP_SETUP_ICU(INTL_SHARED_LIBADD)
+ PHP_SUBST(INTL_SHARED_LIBADD)
+ PHP_REQUIRE_CXX()
+ if test "$icu_version" -ge "4002"; then
+ icu_spoof_src=" spoofchecker/spoofchecker_class.c \
+ spoofchecker/spoofchecker.c\
+ spoofchecker/spoofchecker_create.c\
+ spoofchecker/spoofchecker_main.c"
+ else
+ icu_spoof_src=""
+ fi
+ PHP_NEW_EXTENSION(intl, php_intl.c \
+ intl_error.c \
+ intl_convert.c \
+ collator/collator.c \
+ collator/collator_class.c \
+ collator/collator_sort.c \
+ collator/collator_convert.c \
+ collator/collator_locale.c \
+ collator/collator_compare.c \
+ collator/collator_attr.c \
+ collator/collator_create.c \
+ collator/collator_is_numeric.c \
+ collator/collator_error.c \
+ common/common_error.c \
+ formatter/formatter.c \
+ formatter/formatter_main.c \
+ formatter/formatter_class.c \
+ formatter/formatter_attr.c \
+ formatter/formatter_data.c \
+ formatter/formatter_format.c \
+ formatter/formatter_parse.c \
+ normalizer/normalizer.c \
+ normalizer/normalizer_class.c \
+ normalizer/normalizer_normalize.c \
+ locale/locale.c \
+ locale/locale_class.c \
+ locale/locale_methods.c \
+ dateformat/dateformat.c \
+ dateformat/dateformat_class.c \
+ dateformat/dateformat_attr.c \
+ dateformat/dateformat_data.c \
+ dateformat/dateformat_format.c \
+ dateformat/dateformat_parse.c \
+ msgformat/msgformat.c \
+ msgformat/msgformat_attr.c \
+ msgformat/msgformat_class.c \
+ msgformat/msgformat_data.c \
+ msgformat/msgformat_format.c \
+ msgformat/msgformat_helpers.cpp \
+ msgformat/msgformat_parse.c \
+ grapheme/grapheme_string.c \
+ grapheme/grapheme_util.c \
+ resourcebundle/resourcebundle.c \
+ resourcebundle/resourcebundle_class.c \
+ resourcebundle/resourcebundle_iterator.c \
+ transliterator/transliterator.c \
+ transliterator/transliterator_class.c \
+ transliterator/transliterator_methods.c \
+ idn/idn.c \
+ $icu_spoof_src, $ext_shared,,$ICU_INCS)
+ PHP_ADD_BUILD_DIR($ext_builddir/collator)
+ PHP_ADD_BUILD_DIR($ext_builddir/common)
+ PHP_ADD_BUILD_DIR($ext_builddir/formatter)
+ PHP_ADD_BUILD_DIR($ext_builddir/normalizer)
+ PHP_ADD_BUILD_DIR($ext_builddir/dateformat)
+ PHP_ADD_BUILD_DIR($ext_builddir/locale)
+ PHP_ADD_BUILD_DIR($ext_builddir/msgformat)
+ PHP_ADD_BUILD_DIR($ext_builddir/grapheme)
+ PHP_ADD_BUILD_DIR($ext_builddir/resourcebundle)
+ PHP_ADD_BUILD_DIR($ext_builddir/transliterator)
+ PHP_ADD_BUILD_DIR($ext_builddir/idn)
+ PHP_ADD_BUILD_DIR($ext_builddir/spoofchecker)
+fi
diff --git a/ext/intl/config.w32 b/ext/intl/config.w32
new file mode 100644
index 0000000..437fedb
--- /dev/null
+++ b/ext/intl/config.w32
@@ -0,0 +1,95 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_ENABLE("intl", "Enable internationalization support", "no");
+
+if (PHP_INTL != "no") {
+ if (CHECK_LIB("icuuc.lib", "intl", PHP_INTL) &&
+ CHECK_HEADER_ADD_INCLUDE("unicode/utf.h", "CFLAGS_INTL")) {
+ // always build as shared - zend_strtod.c/ICU type conflict
+ EXTENSION("intl", "php_intl.c intl_convert.c intl_error.c ", true,
+ "/I \"" + configure_module_dirname + "\"");
+ ADD_SOURCES(configure_module_dirname + "/collator", "\
+ collator.c \
+ collator_attr.c \
+ collator_class.c \
+ collator_compare.c \
+ collator_convert.c \
+ collator_create.c \
+ collator_error.c \
+ collator_is_numeric.c \
+ collator_locale.c \
+ collator_sort.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/common", "\
+ common_error.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/formatter", "\
+ formatter.c \
+ formatter_attr.c \
+ formatter_class.c \
+ formatter_data.c \
+ formatter_format.c \
+ formatter_main.c \
+ formatter_parse.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/locale", "\
+ locale.c \
+ locale_class.c \
+ locale_methods.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/msgformat", "\
+ msgformat.c \
+ msgformat_attr.c \
+ msgformat_class.c \
+ msgformat_data.c \
+ msgformat_format.c \
+ msgformat_helpers.cpp \
+ msgformat_parse.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/grapheme", "\
+ grapheme_string.c grapheme_util.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/normalizer", "\
+ normalizer.c \
+ normalizer_class.c \
+ normalizer_normalize.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/dateformat", "\
+ dateformat.c \
+ dateformat_class.c \
+ dateformat_attr.c \
+ dateformat_format.c \
+ dateformat_parse.c \
+ dateformat_data.c \
+ ", "intl");
+ ADD_SOURCES(configure_module_dirname + "/idn", "\
+ idn.c",
+ "intl");
+ ADD_SOURCES(configure_module_dirname + "/resourcebundle", "\
+ resourcebundle.c \
+ resourcebundle_class.c \
+ resourcebundle_iterator.c",
+ "intl");
+
+ if (CHECK_HEADER_ADD_INCLUDE("unicode/uspoof.h", "CFLAGS_INTL")) {
+ ADD_SOURCES(configure_module_dirname + "/spoofchecker", "\
+ spoofchecker.c \
+ spoofchecker_class.c \
+ spoofchecker_create.c \
+ spoofchecker_main.c",
+ "intl");
+ AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled");
+ }
+
+ ADD_SOURCES(configure_module_dirname + "/transliterator", "\
+ transliterator.c \
+ transliterator_class.c \
+ transliterator_methods.c",
+ "intl");
+ ADD_FLAG("LIBS_INTL", "icudt.lib icuin.lib icuio.lib icule.lib iculx.lib");
+ AC_DEFINE("HAVE_INTL", 1, "Internationalization support enabled");
+ } else {
+ 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 100644
index 0000000..8aded18
--- /dev/null
+++ b/ext/intl/dateformat/dateformat.c
@@ -0,0 +1,272 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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
+}
+/* }}} */
+
+/* {{{ */
+static void datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
+{
+ char* locale;
+ int locale_len = 0;
+ zval* object;
+ long date_type = 0;
+ long time_type = 0;
+ long calendar = UCAL_GREGORIAN;
+ 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* dfo;
+
+ intl_error_reset( NULL TSRMLS_CC );
+ object = return_value;
+ /* 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 parameters", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value);
+
+ if (calendar != UCAL_TRADITIONAL && calendar != UCAL_GREGORIAN) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: "
+ "invalid value for calendar type; it must be one of "
+ "IntlDateFormatter::TRADITIONAL (locale's default calendar) "
+ "or IntlDateFormatter::GREGORIAN", 0 TSRMLS_CC);
+ goto error;
+ }
+
+ DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ if (DATE_FORMAT_OBJECT(dfo) != NULL) {
+ intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR,
+ "datefmt_create: cannot call constructor twice", 0 TSRMLS_CC);
+ return;
+ }
+
+ /* 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(dfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ /* object construction -> only set global error */
+ intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: "
+ "error converting pattern to UTF-16", 0 TSRMLS_CC);
+ goto error;
+ }
+ }
+
+ /* resources allocated from now on */
+
+ /* 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(dfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: "
+ "error converting timezone_str to UTF-16", 0 TSRMLS_CC);
+ goto error;
+ }
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ if( pattern_str && pattern_str_len>0 ){
+ DATE_FORMAT_OBJECT(dfo) = udat_open(UDAT_IGNORE, UDAT_IGNORE, locale, timezone_utf16, timezone_utf16_len, svalue, slength, &INTL_DATA_ERROR_CODE(dfo));
+ } else {
+ DATE_FORMAT_OBJECT(dfo) = udat_open(time_type, date_type, locale, timezone_utf16, timezone_utf16_len, svalue, slength, &INTL_DATA_ERROR_CODE(dfo));
+ }
+
+ if (!U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ if (calendar != UCAL_TRADITIONAL) {
+ ucal_obj = ucal_open(timezone_utf16, timezone_utf16_len, locale,
+ calendar, &INTL_DATA_ERROR_CODE(dfo));
+ if (!U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ udat_setCalendar(DATE_FORMAT_OBJECT(dfo), ucal_obj);
+ ucal_close(ucal_obj);
+ } else {
+ intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create"
+ ": error opening calendar", 0 TSRMLS_CC);
+ goto error;
+ }
+ }
+ } else {
+ intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: date "
+ "formatter creation failed", 0 TSRMLS_CC);
+ goto error;
+ }
+
+ /* Set the class variables */
+ dfo->date_type = date_type;
+ dfo->time_type = time_type;
+ dfo->calendar = calendar;
+ if( timezone_str && timezone_str_len > 0){
+ dfo->timezone_id = estrndup( timezone_str, timezone_str_len);
+ }
+
+error:
+ if (svalue) {
+ efree(svalue);
+ }
+ if (timezone_utf16) {
+ efree(timezone_utf16);
+ }
+ if (U_FAILURE(intl_error_get_code(NULL TSRMLS_CC))) {
+ /* free_object handles partially constructed instances fine */
+ zval_dtor(return_value);
+ RETVAL_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ 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 )
+{
+ object_init_ex( return_value, IntlDateFormatter_ce_ptr );
+ datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto void IntlDateFormatter::__construct(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern])
+ * IntlDateFormatter object constructor.
+ */
+PHP_METHOD( IntlDateFormatter, __construct )
+{
+ /* return_value param is being changed, therefore we will always return
+ * NULL here */
+ return_value = getThis();
+ datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ 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 )
+{
+ 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_error_code: unable to parse input params", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ dfo = (IntlDateFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ /* Return formatter's last error code. */
+ RETURN_LONG( INTL_DATA_ERROR_CODE(dfo) );
+}
+/* }}} */
+
+/* {{{ 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;
+ 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_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ dfo = (IntlDateFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ /* Return last error message. */
+ message = intl_error_get_message( INTL_DATA_ERROR_P(dfo) TSRMLS_CC );
+ RETURN_STRING( message, 0);
+}
+/* }}} */
diff --git a/ext/intl/dateformat/dateformat.h b/ext/intl/dateformat/dateformat.h
new file mode 100644
index 0000000..f11918b
--- /dev/null
+++ b/ext/intl/dateformat/dateformat.h
@@ -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 100644
index 0000000..b8c5f25
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_attr.c
@@ -0,0 +1,412 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 "dateformat_class.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 *dfo, 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;
+
+ 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(dfo));
+ INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
+
+ /* Get the locale for the dateformatter */
+ locale = (char *)udat_getLocaleByType(DATE_FORMAT_OBJECT(dfo), ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(dfo));
+
+ /* Set the calendar if passed */
+ ucal_obj = ucal_open(timezone_utf16, timezone_utf16_len, locale, calendar, &INTL_DATA_ERROR_CODE(dfo) );
+ udat_setCalendar( DATE_FORMAT_OBJECT(dfo), ucal_obj );
+ INTL_METHOD_CHECK_STATUS(dfo, "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(dfo, "Error getting formatter datetype." );
+
+ RETURN_LONG(dfo->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(dfo, "Error getting formatter timetype." );
+
+ RETURN_LONG(dfo->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(dfo, "Error getting formatter calendar." );
+
+ RETURN_LONG(dfo->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(dfo, "Error getting formatter timezone_id." );
+
+ if( dfo->timezone_id ){
+ RETURN_STRING((char*)dfo->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( dfo, timezone_id, timezone_id_len, dfo->calendar, return_value TSRMLS_CC );
+
+ /* Set the IntlDateFormatter variable */
+ if( dfo->timezone_id ){
+ efree(dfo->timezone_id);
+ }
+ dfo->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(dfo), is_pattern_localized, value, length, &INTL_DATA_ERROR_CODE(dfo));
+ if(INTL_DATA_ERROR_CODE(dfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
+ ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
+ INTL_DATA_ERROR_CODE(dfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = udat_toPattern(DATE_FORMAT_OBJECT(dfo), is_pattern_localized, value, length, &INTL_DATA_ERROR_CODE(dfo) );
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS(dfo, "Error getting formatter pattern" );
+
+ INTL_METHOD_RETVAL_UTF8( dfo, 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(dfo));
+ INTL_METHOD_CHECK_STATUS(dfo, "Error converting pattern to UTF-16" );
+
+ udat_applyPattern(DATE_FORMAT_OBJECT(dfo), (UBool)is_pattern_localized, svalue, slength);
+
+ if (svalue) {
+ efree(svalue);
+ }
+ INTL_METHOD_CHECK_STATUS(dfo, "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(dfo), loc_type,&INTL_DATA_ERROR_CODE(dfo));
+ INTL_METHOD_CHECK_STATUS(dfo, "Error getting locale");
+ 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(dfo)));
+}
+/* }}} */
+
+/* {{{ 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(dfo), (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( dfo, dfo->timezone_id, strlen(dfo->timezone_id), calendar, return_value TSRMLS_CC );
+
+ /* Set the calendar value in the IntlDateFormatter object */
+ dfo->calendar = calendar;
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+
diff --git a/ext/intl/dateformat/dateformat_attr.h b/ext/intl/dateformat/dateformat_attr.h
new file mode 100644
index 0000000..bf28824
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_attr.h
@@ -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 100644
index 0000000..49f316f
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_class.c
@@ -0,0 +1,208 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+#include <zend_exceptions.h>
+
+zend_class_entry *IntlDateFormatter_ce_ptr = NULL;
+static zend_object_handlers IntlDateFormatter_handlers;
+
+/*
+ * 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* dfo = (IntlDateFormatter_object*)object;
+
+ zend_object_std_dtor( &dfo->zo TSRMLS_CC );
+
+ dateformat_data_free( &dfo->datef_data TSRMLS_CC );
+
+ if( dfo->timezone_id ){
+ efree(dfo->timezone_id);
+ }
+
+ efree( dfo );
+}
+/* }}} */
+
+/* {{{ 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 );
+ object_properties_init(&intern->zo, ce);
+ 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 = &IntlDateFormatter_handlers;
+
+ return retval;
+}
+/* }}} */
+
+/* {{{ IntlDateFormatter_object_clone */
+zend_object_value IntlDateFormatter_object_clone(zval *object TSRMLS_DC)
+{
+ zend_object_value new_obj_val;
+ zend_object_handle handle = Z_OBJ_HANDLE_P(object);
+ IntlDateFormatter_object *dfo, *new_dfo;
+
+ DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ new_obj_val = IntlDateFormatter_ce_ptr->create_object(Z_OBJCE_P(object) TSRMLS_CC);
+ new_dfo = (IntlDateFormatter_object *)zend_object_store_get_object_by_handle(new_obj_val.handle TSRMLS_CC);
+ /* clone standard parts */
+ zend_objects_clone_members(&new_dfo->zo, new_obj_val, &dfo->zo, handle TSRMLS_CC);
+ /* clone formatter object */
+ if (dfo->datef_data.udatf != NULL) {
+ DATE_FORMAT_OBJECT(new_dfo) = udat_clone(DATE_FORMAT_OBJECT(dfo), &INTL_DATA_ERROR_CODE(dfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
+ /* set up error in case error handler is interested */
+ intl_errors_set(INTL_DATA_ERROR_P(dfo), INTL_DATA_ERROR_CODE(dfo),
+ "Failed to clone IntlDateFormatter object", 0 TSRMLS_CC );
+ zend_throw_exception(NULL, "Failed to clone IntlDateFormatter object", 0 TSRMLS_CC);
+ }
+ } else {
+ zend_throw_exception(NULL, "Cannot clone unconstructed IntlDateFormatter", 0 TSRMLS_CC);
+ }
+ return new_obj_val;
+}
+/* }}} */
+
+/*
+ * 'IntlDateFormatter' class registration structures & functions
+ */
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(datefmt_parse_args, 0, 0, 1)
+ ZEND_ARG_INFO(0, string)
+ ZEND_ARG_INFO(1, position)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_format, 0, 0, 0)
+ ZEND_ARG_INFO(0, args)
+ ZEND_ARG_INFO(0, array)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_intldateformatter_getdatetype, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_settimezoneid, 0, 0, 1)
+ ZEND_ARG_INFO(0, zone)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_setpattern, 0, 0, 1)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_setlenient, 0, 0, 1)
+ ZEND_ARG_INFO(0, lenient)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter_setcalendar, 0, 0, 1)
+ ZEND_ARG_INFO(0, which)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_intldateformatter___construct, 0, 0, 3)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, datetype)
+ ZEND_ARG_INFO(0, timetype)
+ ZEND_ARG_INFO(0, timezone)
+ ZEND_ARG_INFO(0, calendar)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ IntlDateFormatter_class_functions
+ * Every 'IntlDateFormatter' class method has an entry in this table
+ */
+static zend_function_entry IntlDateFormatter_class_functions[] = {
+ PHP_ME( IntlDateFormatter, __construct, arginfo_intldateformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( datefmt_create ), arginfo_intldateformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( getDateType, ZEND_FN( datefmt_get_datetype ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( getTimeType, ZEND_FN( datefmt_get_timetype ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( getCalendar, ZEND_FN( datefmt_get_calendar ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( setCalendar, ZEND_FN( datefmt_set_calendar ), arginfo_intldateformatter_setcalendar )
+ PHP_NAMED_FE( getTimeZoneId, ZEND_FN( datefmt_get_timezone_id ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( setTimeZoneId, ZEND_FN( datefmt_set_timezone_id ), arginfo_intldateformatter_settimezoneid )
+ PHP_NAMED_FE( setPattern, ZEND_FN( datefmt_set_pattern ), arginfo_intldateformatter_setpattern )
+ PHP_NAMED_FE( getPattern, ZEND_FN( datefmt_get_pattern ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( getLocale, ZEND_FN( datefmt_get_locale ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( setLenient, ZEND_FN( datefmt_set_lenient ), arginfo_intldateformatter_setlenient )
+ PHP_NAMED_FE( isLenient, ZEND_FN( datefmt_is_lenient ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( format, ZEND_FN( datefmt_format ), arginfo_intldateformatter_format )
+ PHP_NAMED_FE( parse, ZEND_FN( datefmt_parse), datefmt_parse_args )
+ PHP_NAMED_FE( localtime, ZEND_FN( datefmt_localtime ), datefmt_parse_args )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( datefmt_get_error_code ), arginfo_intldateformatter_getdatetype )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( datefmt_get_error_message ), arginfo_intldateformatter_getdatetype )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 );
+
+ memcpy(&IntlDateFormatter_handlers, zend_get_std_object_handlers(),
+ sizeof IntlDateFormatter_handlers);
+ IntlDateFormatter_handlers.clone_obj = IntlDateFormatter_object_clone;
+
+ /* 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 100644
index 0000000..d58abe4
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_class.h
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: 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, dfo)
+#define DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(IntlDateFormatter, dfo)
+#define DATE_FORMAT_METHOD_FETCH_OBJECT \
+ DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK; \
+ if (dfo->datef_data.udatf == NULL) \
+ { \
+ intl_errors_set(&dfo->datef_data.error, U_ILLEGAL_ARGUMENT_ERROR, "Found unconstructed IntlDateFormatter", 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ }
+
+#define DATE_FORMAT_OBJECT(dfo) (dfo)->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 100644
index 0000000..33451e1
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_data.c
@@ -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 100644
index 0000000..cde9e36
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_data.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: 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 100644
index 0000000..4d03d92
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_format.c
@@ -0,0 +1,194 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+#include "ext/date/php_date.h"
+
+/* {{{
+ * Internal function which calls the udat_format
+*/
+static void internal_format(IntlDateFormatter_object *dfo, UDate timestamp, zval *return_value TSRMLS_DC)
+{
+ UChar* formatted = NULL;
+ int32_t resultlengthneeded =0 ;
+
+ resultlengthneeded=udat_format( DATE_FORMAT_OBJECT(dfo), timestamp, NULL, resultlengthneeded, NULL, &INTL_DATA_ERROR_CODE(dfo));
+ if(INTL_DATA_ERROR_CODE(dfo)==U_BUFFER_OVERFLOW_ERROR)
+ {
+ INTL_DATA_ERROR_CODE(dfo)=U_ZERO_ERROR;
+ formatted=(UChar*)emalloc(sizeof(UChar) * resultlengthneeded);
+ udat_format( DATE_FORMAT_OBJECT(dfo), timestamp, formatted, resultlengthneeded, NULL, &INTL_DATA_ERROR_CODE(dfo));
+ }
+
+ if (formatted && U_FAILURE( INTL_DATA_ERROR_CODE(dfo) ) ) {
+ efree(formatted);
+ }
+
+ INTL_METHOD_CHECK_STATUS( dfo, "Date formatting failed" );
+ INTL_METHOD_RETVAL_UTF8( dfo, 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 *dfo, 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 sets UCalendar from the passed array and retrieves timestamp
+*/
+static UDate internal_get_timestamp(IntlDateFormatter_object *dfo, HashTable* hash_arr 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;
+ UCalendar *pcal;
+
+ /* Fetch values from the incoming array */
+ year = internal_get_arr_ele( dfo, 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( dfo, hash_arr, CALENDAR_MON TSRMLS_CC);
+ hour = internal_get_arr_ele( dfo, hash_arr, CALENDAR_HOUR TSRMLS_CC);
+ minute = internal_get_arr_ele( dfo, hash_arr, CALENDAR_MIN TSRMLS_CC);
+ second = internal_get_arr_ele( dfo, hash_arr, CALENDAR_SEC TSRMLS_CC);
+ wday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_WDAY TSRMLS_CC);
+ yday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_YDAY TSRMLS_CC);
+ isInDST = internal_get_arr_ele( dfo, hash_arr, CALENDAR_ISDST TSRMLS_CC);
+ /* For the ucal_setDateTime() function, this is the 'date' value */
+ mday = internal_get_arr_ele( dfo, hash_arr, CALENDAR_MDAY TSRMLS_CC);
+
+ pcal = udat_getCalendar(DATE_FORMAT_OBJECT(dfo));
+ /* set the incoming values for the calendar */
+ ucal_setDateTime( pcal, year, month, mday, hour, minute, second, &INTL_DATA_ERROR_CODE(dfo));
+ if( INTL_DATA_ERROR_CODE(dfo) != U_ZERO_ERROR){
+ return 0;
+ }
+
+ /* Fetch the timestamp from the UCalendar */
+ return ucal_getMillis(pcal, &INTL_DATA_ERROR_CODE(dfo) );
+}
+
+
+/* {{{ 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;
+ 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;
+
+ timestamp = internal_get_timestamp(dfo, hash_arr TSRMLS_CC);
+ INTL_METHOD_CHECK_STATUS( dfo, "datefmt_format: Date formatting failed" )
+ break;
+ case IS_OBJECT: {
+ zend_class_entry *date_ce = php_date_get_date_ce();
+ zval retval;
+ zval *zfuncname;
+ if(!instanceof_function(Z_OBJCE_P(zarg), date_ce TSRMLS_CC)) {
+ intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: object must be an instance of DateTime", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ INIT_ZVAL(retval);
+ MAKE_STD_ZVAL(zfuncname);
+ ZVAL_STRING(zfuncname, "getTimestamp", 1);
+ if(call_user_function(NULL, &zarg, zfuncname, &retval, 0, NULL TSRMLS_CC) != SUCCESS || Z_TYPE(retval) != IS_LONG) {
+ intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, "datefmt_format: cannot get timestamp", 0 TSRMLS_CC );
+ zval_ptr_dtor(&zfuncname);
+ RETURN_FALSE;
+ }
+ zval_ptr_dtor(&zfuncname);
+ p_timestamp = Z_LVAL(retval);
+ timestamp = p_timestamp*1000;
+ }
+ break;
+ default:
+ intl_errors_set( INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR,
+ "datefmt_format: takes either an array or an integer timestamp value or a DateTime object", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ internal_format( dfo, timestamp, return_value TSRMLS_CC);
+
+}
+
+/* }}} */
+
diff --git a/ext/intl/dateformat/dateformat_format.h b/ext/intl/dateformat/dateformat_format.h
new file mode 100644
index 0000000..49f34c6
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_format.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef 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 100644
index 0000000..4193e89
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_parse.c
@@ -0,0 +1,199 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 <math.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 *dfo, char* text_to_parse, int32_t text_len, int32_t *parse_pos, zval *return_value TSRMLS_DC)
+{
+ double 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(dfo));
+ INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
+
+ timestamp = udat_parse( DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
+ if( text_utf16 ){
+ efree(text_utf16);
+ }
+
+ INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" );
+
+ /* Since return is in sec. */
+ result = (double)timestamp / U_MILLIS_PER_SECOND;
+ if(result > LONG_MAX || result < -LONG_MAX) {
+ ZVAL_DOUBLE(return_value, result<0?ceil(result):floor(result));
+ } else {
+ ZVAL_LONG(return_value, (long)result);
+ }
+}
+/* }}} */
+
+static void add_to_localtime_arr( IntlDateFormatter_object *dfo, 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(dfo));
+ INTL_METHOD_CHECK_STATUS( dfo, "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 *dfo, char* text_to_parse, int32_t text_len, int32_t *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(dfo));
+ INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
+
+ parsed_calendar = udat_getCalendar(DATE_FORMAT_OBJECT(dfo));
+ udat_parseCalendar( DATE_FORMAT_OBJECT(dfo), parsed_calendar, text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
+
+ if (text_utf16) {
+ efree(text_utf16);
+ }
+
+ INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" );
+
+
+ array_init( return_value );
+ /* Add entries from various fields of the obtained parsed_calendar */
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_SECOND, CALENDAR_SEC TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MINUTE, CALENDAR_MIN TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_HOUR_OF_DAY, CALENDAR_HOUR TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_YEAR, CALENDAR_YEAR TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_MONTH, CALENDAR_MDAY TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_WEEK, CALENDAR_WDAY TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_DAY_OF_YEAR, CALENDAR_YDAY TSRMLS_CC);
+ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MONTH, CALENDAR_MON TSRMLS_CC);
+
+ /* Is in DST? */
+ isInDST = ucal_inDaylightTime(parsed_calendar , &INTL_DATA_ERROR_CODE(dfo));
+ INTL_METHOD_CHECK_STATUS( dfo, "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;
+ zval* z_parse_pos = NULL;
+ int32_t parse_pos = -1;
+
+ DATE_FORMAT_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z!",
+ &object, IntlDateFormatter_ce_ptr, &text_to_parse, &text_len, &z_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;
+
+ if (z_parse_pos) {
+ convert_to_long(z_parse_pos);
+ parse_pos = (int32_t)Z_LVAL_P(z_parse_pos);
+ if(parse_pos > text_len) {
+ RETURN_FALSE;
+ }
+ }
+ internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos?&parse_pos:NULL, return_value TSRMLS_CC);
+ if(z_parse_pos) {
+ zval_dtor(z_parse_pos);
+ ZVAL_LONG(z_parse_pos, parse_pos);
+ }
+}
+/* }}} */
+
+/* {{{ 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;
+ zval* z_parse_pos = NULL;
+ int32_t parse_pos = -1;
+
+ DATE_FORMAT_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|z!",
+ &object, IntlDateFormatter_ce_ptr, &text_to_parse, &text_len, &z_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;
+
+ if(z_parse_pos) {
+ convert_to_long(z_parse_pos);
+ parse_pos = (int32_t)Z_LVAL_P(z_parse_pos);
+ if(parse_pos > text_len) {
+ RETURN_FALSE;
+ }
+ }
+ internal_parse_to_localtime( dfo, text_to_parse, text_len, z_parse_pos?&parse_pos:NULL, return_value TSRMLS_CC);
+ if(z_parse_pos) {
+ zval_dtor(z_parse_pos);
+ ZVAL_LONG(z_parse_pos, parse_pos);
+ }
+}
+/* }}} */
+
diff --git a/ext/intl/dateformat/dateformat_parse.h b/ext/intl/dateformat/dateformat_parse.h
new file mode 100644
index 0000000..c74a3d5
--- /dev/null
+++ b/ext/intl/dateformat/dateformat_parse.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: 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 100644
index 0000000..4a66dc1
--- /dev/null
+++ b/ext/intl/doc/Tutorial.txt
@@ -0,0 +1,239 @@
+1. Collator::getAvailableLocales().
+Return the locales available at the time of the call, including registered locales.
+If a sever error occurs (such as out of memory condition) this will return null.
+If there is no locale data, an empty enumeration will be returned.
+Returned locales list is a strings in format of RFC4646 standart (see http://www.rfc-editor.org/rfc/rfc4646.txt).
+Examle of locales format: 'en_US', 'ru_UA', 'ua_UA' (see http://demo.icu-project.org/icu-bin/locexp).
+
+
+2. Collator::getDisplayName( $obj_locale, $disp_locale ).
+Get name of the object for the desired Locale, in the desired langauge. Both arguments
+must be from getAvailableLocales method.
+
+ @param string $obj_locale Locale to get display name for.
+ @param string $disp_locale Specifies the desired locale for output
+
+Both parameters are case insensitive.
+For locale format see RFC4647 standart in ftp://ftp.rfc-editor.org/in-notes/rfc4647.txt
+
+3. Collator::getLocaleByType( $type ).
+Allow user to select whether she wants information on requested, valid or actual locale.
+Returned locale tag is a string formatted to a RFC4646 standart and normalize to normal form -
+value is a string from
+For example, a collator for "en_US_CALIFORNIA" was requested. In the current state of ICU (2.0),
+the requested locale is "en_US_CALIFORNIA", the valid locale is "en_US" (most specific locale
+supported by ICU) and the actual locale is "root" (the collation data comes unmodified from the UCA)
+The locale is considered supported by ICU if there is a core ICU bundle for that locale (although
+it may be empty).
+
+
+4. VariableTop
+The Variable_Top attribute is only meaningful if the Alternate attribute is not set to NonIgnorable.
+In such a case, it controls which characters count as ignorable. The string value specifies
+the "highest" character (in UCA order) weight that is to be considered ignorable.
+Thus, for example, if a user wanted whitespace to be ignorable, but not any visible characters,
+then s/he would use the value Variable_Top="\u0020" (space). The string should only be a
+single character. All characters of the same primary weight are equivalent, so
+Variable_Top="\u3000" (ideographic space) has the same effect as Variable_Top="\u0020".
+This setting (alone) has little impact on string comparison performance; setting it lower or higher
+will make sort keys slightly shorter or longer respectively.
+
+
+5. Strength
+The ICU Collation Service supports many levels of comparison (named "Levels", but also
+known as "Strengths"). Having these categories enables ICU to sort strings precisely
+according to local conventions. However, by allowing the levels to be selectively
+employed, searching for a string in text can be performed with various matching
+conditions.
+Performance optimizations have been made for ICU collation with the default level
+settings. Performance specific impacts are discussed in the Performance section below.
+Following is a list of the names for each level and an example usage:
+
+1. Primary Level: Typically, this is used to denote differences between base characters
+(for example, "a" < "b"). It is the strongest difference. For example, dictionaries are
+divided into different sections by base character. This is also called the level1
+strength.
+
+2. Secondary Level: Accents in the characters are considered secondary differences (for
+example, "as" < "as" < "at"). Other differences between letters can also be considered
+secondary differences, depending on the language. A secondary difference is ignored
+when there is a primary difference anywhere in the strings. This is also called the
+level2 strength.
+Note: In some languages (such as Danish), certain accented letters are considered to
+be separate base characters. In most languages, however, an accented letter only has a
+secondary difference from the unaccented version of that letter.
+
+3. Tertiary Level: Upper and lower case differences in characters are distinguished at the
+tertiary level (for example, "ao" < "Ao" < "ao"). In addition, a variant of a letter differs
+from the base form on the tertiary level (such as "A" and " "). Another ? example is the
+difference between large and small Kana. A tertiary difference is ignored when there is
+a primary or secondary difference anywhere in the strings. This is also called the level3
+strength.
+
+4. Quaternary Level: When punctuation is ignored (see Ignoring Punctuations ) at level
+13, an additional level can be used to distinguish words with and without punctuation
+(for example, "ab" < "a-b" < "aB"). This difference is ignored when there is a primary,
+secondary or tertiary difference. This is also known as the level4 strength. The
+quaternary level should only be used if ignoring punctuation is required or when
+processing Japanese text (see Hiragana processing).
+
+5. Identical Level: When all other levels are equal, the identical level is used as a
+tiebreaker. The Unicode code point values of the NFD form of each string are
+compared at this level, just in case there is no difference at levels 14
+. For example, Hebrew cantillation marks are only distinguished at this level. This level should be
+used sparingly, as only code point values differences between two strings is an
+extremely rare occurrence. Using this level substantially decreases the performance for
+both incremental comparison and sort key generation (as well as increasing the sort
+key length). It is also known as level 5 strength.
+
+For example, people may choose to ignore accents or ignore accents and case when searching
+for text. Almost all characters are distinguished by the first three levels, and in most
+locales the default value is thus Tertiary. However, if Alternate is set to be Shifted,
+then the Quaternary strength can be used to break ties among whitespace, punctuation, and
+symbols that would otherwise be ignored. If very fine distinctions among characters are required,
+then the Identical strength can be used (for example, Identical Strength distinguishes
+between the Mathematical Bold Small A and the Mathematical Italic Small A.). However, using
+levels higher than Tertiary the Identical strength result in significantly longer sort
+keys, and slower string comparison performance for equal strings.
+
+
+
+6. Collator::__construct( $locale ).
+The Locale attribute is typically the most important attribute for correct sorting and matching,
+according to the user expectations in different countries and regions. The default UCA
+ordering will only sort a few languages such as Dutch and Portuguese correctly ("correctly"
+meaning according to the normal expectations for users of the languages).
+Otherwise, you need to supply the locale to UCA in order to properly collate text for a
+given language. Thus a locale needs to be supplied so as to choose a collator that is correctly
+tailored for that locale. The choice of a locale will automatically preset the values for
+all of the attributes to something that is reasonable for that locale. Thus most of the time the
+other attributes do not need to be explicitly set. In some cases, the choice of locale will make a
+difference in string comparison performance and/or sort key length.
+In short attribute names, <language>_<script>_<region>_<keyword>.
+Not all the elements are required. Valid values for locale elements are general valid values
+for RFC4646 locale naming, and RFC 4647 lookup algorithm.
+Example:
+Locale="sv" (Swedish) "Kypper" < "Kopfe"
+Locale="de" (German) "Kopfe" < "Kypper"
+
+
+7. Collator::get/setAttribute.
+ICU uses UCA as a default starting point for ordering. Not all languages have sorting sequences
+that correspond with the UCA because UCA cannot simultaneously encompass the specifics of all
+the languages currently in use. Therefore, ICU provides a data-driven, flexible, and run-time
+customizable mechanism called "tailoring". Tailoring overrides the default order of code points
+and the values of the ICU Collation Service attributes.
+Collator have followed attributes:
+ - FRENCH_COLLATION, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+ - CASE_FIRST, possible values are:
+ OFF (default)
+ LOWER_FIRST
+ UPPER_FIRST
+ DEFAULT
+
+ - CASE_LEVEL, possible values are:
+ OFF (default)
+ ON
+ DEFAULT
+
+ - NORMALIZATION_MODE, possible values are:
+ OFF (default)
+ ON
+ DEFAULT
+
+ - STRENGTH, possible values are:
+ PRIMARY
+ SECONDARY
+ TERTIARY (default)
+ QUATERNARY
+ IDENTICAL
+ DEFAULT
+
+ - ALTERNATE_HANDLING, possible values are:
+ NON_IGNORABLE (default)
+ SHIFTED
+ DEFAULT
+
+ - HIRAGANA_QUATERNARY_MODE, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+ - NUMERIC_COLLATION, possible values are:
+ ON
+ OFF (default)
+ DEFAULT
+
+Description of all of this attributes:
+
+FRENCH_COLLATION - Sort strings with different accents from the back of the string. This attribute
+is automatically set to On for the French locales and a few others. Users normally would
+not need to explicitly set this attribute. There is a string comparison performance cost when
+it is set On, but sort key length is unaffected.
+Example:
+F=X cote < cote < cote < cote
+F=O cote < cote < cote < cote
+
+CASE_FIRST - The Case_First attribute is used to control whether uppercase letters come before
+lowercase letters or vice versa, in the absence of other differences in the strings. The possible
+values are Uppercase_First (U) and Lowercase_First (L), plus the standard Default and Off.
+There is almost no difference between the Off and Lowercase_First options in terms of results,
+so typically users will not use Lowercase_First: only Off or Uppercase_First. (People interested
+in the detailed differences between X and L should consult the Collation Customization).
+Specifying either L or U won't affect string comparison performance, but will affect the sort key
+length.
+Example:
+C=X or C=L "china" < "China" < "denmark" <
+"Denmark"
+C=U "China" < "china" < "Denmark" < "denmark"
+
+CASE_LEVEL - The Case_Level attribute is used when ignoring accents but not case. In such a situation,
+set Strength to be Primary, and Case_Level to be On. In most locales, this setting is Off by default.
+There is a small string comparison performance and sort key impact if this attribute is set to be On.
+Example:
+S=1, E=X role = Role = role
+S=1, E=O role = role < Role
+
+NORMALIZATION_MODE - The Normalization setting determines whether text is thoroughly normalized
+or not in comparison. Even if the setting is off (which is the default for many locales), text as
+represented in common usage will compare correctly (for details, see UTN #5). Only if the accent
+marks are in noncanonical order will there be a problem. If the setting is On, then the best
+results are guaranteed for all possible text input. There is a medium string comparison performance
+cost if this attribute is On, depending on the frequency of sequences that require normalization.
+There is no significant effect on sort key length. If the input text is known to be in NFD or NFKD
+normalization forms, there is no need to enable this Normalization option.
+
+STRENGTH - see Collator::setStrength chapter.
+
+ALTERNATE_HANDLING - The Alternate attribute is used to control the handling of the socalled
+variable characters in the UCA: whitespace, punctuation and symbols. If Alternate is set to
+NonIgnorable (N), then differences among these characters are of the same importance as
+differences among letters. If Alternate is set to Shifted (S), then these characters are of only
+minor importance. The Shifted value is often used in combination with Strength set to Quaternary.
+In such a case, whitespace, punctuation, and symbols are considered when comparing strings,
+but only if all other aspects of the strings (base letters, accents, and case) are identical.
+If Alternate is not set to Shifted, then there is no difference between a Strength of 3 and
+a Strength of 4. For more information and examples, see
+Variable_Weighting in the UCA (http://www.unicode.org/reports/tr10/#Variable_Weighting).
+The reason the Alternate values are not simply On and Off is that additional Alternate values
+may be added in the future. The UCA option Blanked is expressed with Strength set to 3,
+and Alternate set to Shifted. The default for most locales is NonIgnorable. If Shifted is selected,
+it may be slower if there are many strings that are the same except for punctuation;
+sort key length will not be affected unless the strength level is also increased.
+Example:
+S=3, A=N di Silva < Di Silva < diSilva < U.S.A. < USA
+S=3, A=S di Silva = diSilva < Di Silva < U.S.A. = USA
+S=4, A=S di Silva < diSilva < Di Silva < U.S.A. < USA
+
+HIRAGANA_QUATERNARY_MODE - Compatibility with JIS x 4061 requires the introduction of an additional
+level to distinguish Hiragana and Katakana characters. If compatibility with that standard is required,
+then this attribute should be set On, and the strength set to Quaternary. This will affect sort key
+length and string comparison string comparison performance.
+
+NUMERIC_COLLATION - When turned on, this attribute generates a collation key for the
+numeric value of substrings of digits. This is a way to get '100' to sort AFTER '2'.
+
diff --git a/ext/intl/doc/collator_api.php b/ext/intl/doc/collator_api.php
new file mode 100644
index 0000000..ef72502
--- /dev/null
+++ b/ext/intl/doc/collator_api.php
@@ -0,0 +1,397 @@
+<?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 occurred 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 occurred,
+ * 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 occurred,
+ * 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 occurred in the last
+ * Collator API function call.
+ */
+function collator_get_error_message( $coll ) {}
diff --git a/ext/intl/doc/common_api.php b/ext/intl/doc/common_api.php
new file mode 100644
index 0000000..993dab1
--- /dev/null
+++ b/ext/intl/doc/common_api.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Handling of errors occurred 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 occurred 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 100644
index 0000000..272abdb
--- /dev/null
+++ b/ext/intl/doc/datefmt_api.php
@@ -0,0 +1,436 @@
+<?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 occurred. 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 occurred. 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 100644
index 0000000..14d98db
--- /dev/null
+++ b/ext/intl/doc/formatter_api.php
@@ -0,0 +1,500 @@
+<?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 occurred 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 occurred 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 100644
index 0000000..465453f
--- /dev/null
+++ b/ext/intl/doc/grapheme_api.php
@@ -0,0 +1,132 @@
+<?php
+#############################################################################
+# Grapheme constants.
+#############################################################################
+
+ /**
+ * grapheme_extract extract_type
+ *
+ */
+ /** Extract the given number of whole grapheme clusters from the string: */
+ define('GRAPHEME_EXTR_COUNT', 0);
+ /** Extract as many whole grapheme clusters as will fit into the given number of bytes: */
+ define('GRAPHEME_EXTR_MAXBYTES', 1);
+ /** Extract whole grapheme clusters up to a maximum number of UTF-8 characters: */
+ define('GRAPHEME_EXTR_MAXCHARS', 2);
+
+
+#############################################################################
+# Grapheme API
+#############################################################################
+
+ /**
+ * Get string length in grapheme units
+ * @param string $input The string being measured for length.
+ * @return int The length of the string on success, and 0 if the string is empty.
+ */
+ function grapheme_strlen($input) {}
+
+ /**
+ * Find position (in grapheme units) of first occurrence of a string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, strpos() will return boolean FALSE.
+ */
+ function grapheme_strpos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of first occurrence of a case-insensitive string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_stripos() will return boolean FALSE.
+ */
+ function grapheme_stripos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of last occurrence of a string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_strrpos() will return boolean FALSE.
+ */
+ function grapheme_strrpos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Find position (in grapheme units) of last occurrence of a case-insensitive string
+ * @param string $haystack The string to look in
+ * @param string $needle The string to look for
+ * @param [int] $offset The optional offset parameter allows you to specify
+ which character in haystack to start searching. The position
+ returned is still relative to the beginning of haystack.
+ * @return int Returns the position as an integer. If needle is not found, grapheme_strripos() will return boolean FALSE.
+ */
+ function grapheme_strripos($haystack, $needle, $offset = 0) {}
+
+
+ /**
+ * Return part of a string
+ * @param string $string The input string.
+ * @param int $start If start is non-negative, the returned string will start at the
+ start'th position in string, counting from zero. If start is negative,
+ the returned string will start at the start'th character from the
+ end of string.
+ * @param [int] $length If length is given and is positive, the string returned will contain
+ at most length characters beginning from start (depending on the
+ length of string). If string is less than or equal to start characters
+ long, FALSE will be returned. If length is given and is negative, then
+ that many characters will be omitted from the end of string (after the
+ start position has been calculated when a start is negative). If start
+ denotes a position beyond this truncation, an empty string will be returned.
+ * @return int Returns the extracted part of string.
+ */
+ function grapheme_substr($string, $start, $length = -1) {}
+
+
+ /**
+ * Returns part of haystack string from the first occurrence of needle to the end of haystack.
+ * @param string $haystack The input string.
+ * @param string $needle The string to look for.
+ * @param [boolean] $before_needle If TRUE (the default is FALSE), grapheme_strstr() returns the part of the
+ haystack before the first occurence of the needle.
+ * @return string Returns the portion of string, or FALSE if needle is not found.
+ */
+ function grapheme_strstr($haystack, $needle, $before_needle = FALSE) {}
+
+
+ /**
+ * Returns part of haystack string from the first occurrence of case-insensitive needle to the end of haystack.
+ * @param string $haystack The input string.
+ * @param string $needle The string to look for.
+ * @param [boolean] $before_needle If TRUE (the default is FALSE), grapheme_strstr() returns the part of the
+ haystack before the first occurence of the needle.
+ * @return string Returns the portion of string, or FALSE if needle is not found.
+ */
+ function grapheme_stristr($haystack, $needle, $before_needle = FALSE) {}
+
+
+ /**
+ * Function to extract a sequence of default grapheme clusters from a text buffer, which must be encoded in UTF-8.
+ * @param string $haystack string to search
+ * @param int $size maximum number of units - based on the $extract_type - to return
+ * @param [int] $extract_type one of GRAPHEME_EXTR_COUNT (default), GRAPHEME_EXTR_MAXBYTES, or GRAPHEME_EXTR_MAXCHARS
+ * @param [int] $start starting position in $haystack in bytes
+ * @param [&int] $next set to next starting position in bytes
+ * @return string A string starting at offset $start containing no more than $size grapheme clusters
+ and ending on a default grapheme cluster boundary.
+ */
+ function grapheme_extract($haystack, $size, $extract_type = GRAPHEME_EXTR_COUNT, $start = 0, &$next) {}
+
+?>
+
+
diff --git a/ext/intl/doc/locale_api.php b/ext/intl/doc/locale_api.php
new file mode 100644
index 0000000..c6c41ed
--- /dev/null
+++ b/ext/intl/doc/locale_api.php
@@ -0,0 +1,434 @@
+<?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 = null;
+
+ /**
+ * 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
+ */
+ const LANG_TAG = "language";
+ const EXTLANG_TAG = "extlang";
+ const SCRIPT_TAG = "script";
+ const REGION_TAG = "region";
+ const VARIANT_TAG = "variant";
+ const GRANDFATHERED_LANG_TAG = "grandfathered";
+ const 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
+ * @param bool $canonicalize Canonicalize parameters?
+ * @return boolean 'true' if $locale matches $langtag 'false' otherwise
+ */
+ public static function filterMatches($langtag, $locale, $canonicalize) {}
+
+ /**
+ * 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
+ */
+ 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
+ */
+ 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.
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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
+ * @param bool $canonicalize Canonicalize parameters?
+ * @return boolean 'true' if $locale matches $langtag 'false' otherwise
+ */
+ function locale_filter_matches($langtag, $locale, $canonicalize) {}
+
+ /**
+ * 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
+ */
+ 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.
+ */
+ 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.
+ */
+ function locale_parse_locale($locale) {}
+
+?>
diff --git a/ext/intl/doc/msgfmt_api.php b/ext/intl/doc/msgfmt_api.php
new file mode 100644
index 0000000..e4d047b
--- /dev/null
+++ b/ext/intl/doc/msgfmt_api.php
@@ -0,0 +1,209 @@
+<?php
+
+/**
+ * Message formatter class.
+ *
+ * Message Format provides for runtime formatting of messages in a manner
+ * somewhat similar to sprintf. The pattern string has its component parts
+ * replaced in a locale-sensitive manner using items in the arguments array.
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/umsg_8h.html
+ *
+ */
+class MessageFormatter {
+
+ /**
+ * Constructs a new Message Formatter
+ *
+ * @param string $locale the locale to use when formatting arguments
+ * @param string $pattern the pattern string to stick arguments into
+ */
+ public function __construct($locale, $pattern) {}
+
+ /**
+ * Constructs a new Message Formatter
+ *
+ * @param string $locale the locale to use when formatting arguments
+ * @param string $pattern the pattern string to stick arguments into
+ */
+ public static function create($locale, $pattern) {}
+
+ /**
+ * Format the message
+ * @param array $args arguments to insert into the pattern string
+ * @return string the formatted string, or false if an error ocurred
+ */
+ public function format($args) {}
+
+ /**
+ * Parse input string and returns any extracted items as an array
+ *
+ * $error will contain any error code. If an error occurs, $parse_pos contains
+ * the position of the error.
+ *
+ * @param string $value string to parse for items
+ * @return array array containing items extracted
+ *
+ */
+ public function parse($value) {}
+
+ /**
+ * Inserts the items in $args into $pattern, formatting them
+ * according to $locale. This is the static implementation.
+ *
+ * @param string $locale the locale to use when formatting numbers and dates and suchlike
+ * @param string $pattern the pattern string to insert things into
+ * @param array $args the array of values to insert into $pattern
+ * @return string the formatted pattern string or false if an error occurred
+ */
+ 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 occurred
+ */
+ 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 100644
index 0000000..a9bb566
--- /dev/null
+++ b/ext/intl/doc/normalizer_api.php
@@ -0,0 +1,92 @@
+<?php
+
+#############################################################################
+# Object-oriented API
+#############################################################################
+
+/**
+ * Normalizer class.
+ *
+ * Normalizer provides access to Unicode normalization of strings. This class consists
+ * only of static methods. The iterator interface to normalizer is rarely used, so is
+ * not provided here.
+ *
+ * Example:
+ * <code>
+ *
+ * </code>
+ *
+ * @see http://www.icu-project.org/apiref/icu4c/unorm_8h.html
+ * @see http://www.icu-project.org/apiref/icu4c/classNormalizer.html
+ *
+ */
+class Normalizer {
+#############################################################################
+# Common constants.
+#############################################################################
+
+ /**
+ * Valid normalization form values.
+ *
+ * @see Normalizer::normalize()
+ * @see Normalizer::isNormalize()
+ * @see normalizer_normalize()
+ * @see normalizer_is_normalized()
+ */
+ const NONE = 1;
+ /** Canonical decomposition. */
+ const NFD = 2;
+ const FORM_D = NFD;
+ /** Compatibility decomposition. */
+ const NFKD = 3;
+ const FORM_KD = NFKD;
+ /** Canonical decomposition followed by canonical composition. */
+ const NFC = 4;
+ const FORM_C = NFC;
+ /** Compatibility decomposition followed by canonical composition. */
+ const NFKC =5;
+ const FORM_KC = NFKC;
+
+
+ /**
+ * Normalizes the input provided and returns the normalized string
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return string The normalized string or null if an error occurred.
+ */
+ public static function normalize($input, $form = Normalizer::FORM_C) {}
+
+ /**
+ * Checks if the provided string is already in the specified normalization form.
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return boolean True if normalized, false otherwise or if there is an error
+ */
+ public static function isNormalized($input, $form = Normalizer::FORM_C) {}
+
+}
+
+#############################################################################
+# Procedural API
+#############################################################################
+
+ /**
+ * Normalizes the input provided and returns the normalized string
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return string The normalized string or null if an error occurred.
+ */
+ function normalizer_normalize($input, $form = Normalizer::FORM_C) {}
+
+ /**
+ * Checks if the provided string is already in the specified normalization form.
+ * @param string $input The input string to normalize
+ * @param [int] $form One of the normalization forms
+ * @return boolean True if normalized, false otherwise or if there an error
+ */
+ function normalizer_is_normalized($input, $form = Normalizer::FORM_C) {}
+
+
+?>
+
+
diff --git a/ext/intl/formatter/formatter.c b/ext/intl/formatter/formatter.c
new file mode 100644
index 0000000..1b7922c
--- /dev/null
+++ b/ext/intl/formatter/formatter.c
@@ -0,0 +1,152 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/utypes.h>
+#include <unicode/unum.h>
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+
+#if U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM <= 4
+#define UNUM_MONETARY_GROUPING_SEPARATOR_SYMBOL 17
+#endif
+
+/* {{{ formatter_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void formatter_register_constants( INIT_FUNC_ARGS )
+{
+ if( NumberFormatter_ce_ptr == NULL) {
+ zend_error(E_ERROR, "NumberFormatter class not defined");
+ }
+
+ #define FORMATTER_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define FORMATTER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( NumberFormatter_ce_ptr, ZEND_STRS( #x ) - 1, UNUM_##x TSRMLS_CC );
+ #define FORMATTER_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( NumberFormatter_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ /* UNumberFormatStyle constants */
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_DECIMAL );
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY );
+ FORMATTER_EXPOSE_CLASS_CONST( PERCENT );
+ FORMATTER_EXPOSE_CLASS_CONST( SCIENTIFIC );
+ FORMATTER_EXPOSE_CLASS_CONST( SPELLOUT );
+ FORMATTER_EXPOSE_CLASS_CONST( ORDINAL );
+ FORMATTER_EXPOSE_CLASS_CONST( DURATION );
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_RULEBASED );
+ FORMATTER_EXPOSE_CLASS_CONST( IGNORE );
+
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "DEFAULT_STYLE", UNUM_DEFAULT );
+
+/* workaround for ICU bug */
+#if U_ICU_VERSION_MAJOR_NUM == 3 && U_ICU_VERSION_MINOR_NUM < 8
+#define UNUM_ROUND_HALFEVEN UNUM_FOUND_HALFEVEN
+#endif
+
+ /* UNumberFormatRoundingMode */
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_CEILING );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_FLOOR );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_DOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_UP );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFEVEN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFDOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUND_HALFUP );
+
+ /* UNumberFormatPadPosition */
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_BEFORE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_AFTER_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_BEFORE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_AFTER_SUFFIX );
+
+ /* UNumberFormatAttribute */
+ FORMATTER_EXPOSE_CLASS_CONST( PARSE_INT_ONLY );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_USED );
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL_ALWAYS_SHOWN );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( INTEGER_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( FRACTION_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MULTIPLIER );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_SIZE );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUNDING_MODE );
+ FORMATTER_EXPOSE_CLASS_CONST( ROUNDING_INCREMENT );
+ FORMATTER_EXPOSE_CLASS_CONST( FORMAT_WIDTH );
+ FORMATTER_EXPOSE_CLASS_CONST( PADDING_POSITION );
+ FORMATTER_EXPOSE_CLASS_CONST( SECONDARY_GROUPING_SIZE );
+ FORMATTER_EXPOSE_CLASS_CONST( SIGNIFICANT_DIGITS_USED );
+ FORMATTER_EXPOSE_CLASS_CONST( MIN_SIGNIFICANT_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( MAX_SIGNIFICANT_DIGITS );
+ FORMATTER_EXPOSE_CLASS_CONST( LENIENT_PARSE );
+
+ /* UNumberFormatTextAttribute */
+ FORMATTER_EXPOSE_CLASS_CONST( POSITIVE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( POSITIVE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( NEGATIVE_PREFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( NEGATIVE_SUFFIX );
+ FORMATTER_EXPOSE_CLASS_CONST( PADDING_CHARACTER );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY_CODE );
+ FORMATTER_EXPOSE_CLASS_CONST( DEFAULT_RULESET );
+ FORMATTER_EXPOSE_CLASS_CONST( PUBLIC_RULESETS );
+
+ /* UNumberFormatSymbol */
+ FORMATTER_EXPOSE_CLASS_CONST( DECIMAL_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( GROUPING_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PATTERN_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PERCENT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( ZERO_DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MINUS_SIGN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PLUS_SIGN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( CURRENCY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( INTL_CURRENCY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MONETARY_SEPARATOR_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( EXPONENTIAL_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PERMILL_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( PAD_ESCAPE_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( INFINITY_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( NAN_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( SIGNIFICANT_DIGIT_SYMBOL );
+ FORMATTER_EXPOSE_CLASS_CONST( MONETARY_GROUPING_SEPARATOR_SYMBOL );
+
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_DEFAULT", FORMAT_TYPE_DEFAULT );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_INT32", FORMAT_TYPE_INT32 );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_INT64", FORMAT_TYPE_INT64 );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_DOUBLE", FORMAT_TYPE_DOUBLE );
+ FORMATTER_EXPOSE_CUSTOM_CLASS_CONST( "TYPE_CURRENCY", FORMAT_TYPE_CURRENCY );
+
+ #undef FORMATTER_EXPOSE_CUSTOM_CLASS_CONST
+ #undef FORMATTER_EXPOSE_CLASS_CONST
+ #undef FORMATTER_EXPOSE_CONST
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/formatter/formatter.h b/ext/intl/formatter/formatter.h
new file mode 100644
index 0000000..1f9ebea
--- /dev/null
+++ b/ext/intl/formatter/formatter.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_FORMATTER_H
+#define FORMATTER_FORMATTER_H
+
+#include <php.h>
+
+void formatter_register_constants( INIT_FUNC_ARGS );
+
+#endif // FORMATTER_FORMATTER_H
diff --git a/ext/intl/formatter/formatter_attr.c b/ext/intl/formatter/formatter_attr.c
new file mode 100644
index 0000000..448a9db
--- /dev/null
+++ b/ext/intl/formatter/formatter_attr.c
@@ -0,0 +1,459 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_attr.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+
+/* {{{ proto mixed NumberFormatter::getAttribute( int $attr )
+ * Get formatter attribute value. }}} */
+/* {{{ proto mixed numfmt_get_attribute( NumberFormatter $nf, int $attr )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_get_attribute )
+{
+ long attribute, value;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ switch(attribute) {
+ case UNUM_PARSE_INT_ONLY:
+ case UNUM_GROUPING_USED:
+ case UNUM_DECIMAL_ALWAYS_SHOWN:
+ case UNUM_MAX_INTEGER_DIGITS:
+ case UNUM_MIN_INTEGER_DIGITS:
+ case UNUM_INTEGER_DIGITS:
+ case UNUM_MAX_FRACTION_DIGITS:
+ case UNUM_MIN_FRACTION_DIGITS:
+ case UNUM_FRACTION_DIGITS:
+ case UNUM_MULTIPLIER:
+ case UNUM_GROUPING_SIZE:
+ case UNUM_ROUNDING_MODE:
+ case UNUM_FORMAT_WIDTH:
+ case UNUM_PADDING_POSITION:
+ case UNUM_SECONDARY_GROUPING_SIZE:
+ case UNUM_SIGNIFICANT_DIGITS_USED:
+ case UNUM_MIN_SIGNIFICANT_DIGITS:
+ case UNUM_MAX_SIGNIFICANT_DIGITS:
+ case UNUM_LENIENT_PARSE:
+ value = unum_getAttribute(FORMATTER_OBJECT(nfo), attribute);
+ if(value == -1) {
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ } else {
+ RETVAL_LONG(value);
+ }
+ break;
+ case UNUM_ROUNDING_INCREMENT:
+ {
+ double value_double = unum_getDoubleAttribute(FORMATTER_OBJECT(nfo), attribute);
+ if(value_double == -1) {
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ } else {
+ RETVAL_DOUBLE(value_double);
+ }
+ }
+ break;
+ default:
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ break;
+ }
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getTextAttribute( int $attr )
+ * Get formatter attribute value. }}} */
+/* {{{ proto string numfmt_get_text_attribute( NumberFormatter $nf, int $attr )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_get_text_attribute )
+{
+ long attribute;
+ UChar value_buf[64];
+ int value_buf_size = USIZE( value_buf );
+ UChar* value = value_buf;
+ int length = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol",
+ &object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_text_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) );
+ if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= value_buf_size) {
+ ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setAttribute( int $attr, mixed $value )
+ * Get formatter attribute value. }}} */
+/* {{{ proto bool numfmt_set_attribute( NumberFormatter $nf, int $attr, mixed $value )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_set_attribute )
+{
+ long attribute;
+ zval **value;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OlZ",
+ &object, NumberFormatter_ce_ptr, &attribute, &value ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ switch(attribute) {
+ case UNUM_PARSE_INT_ONLY:
+ case UNUM_GROUPING_USED:
+ case UNUM_DECIMAL_ALWAYS_SHOWN:
+ case UNUM_MAX_INTEGER_DIGITS:
+ case UNUM_MIN_INTEGER_DIGITS:
+ case UNUM_INTEGER_DIGITS:
+ case UNUM_MAX_FRACTION_DIGITS:
+ case UNUM_MIN_FRACTION_DIGITS:
+ case UNUM_FRACTION_DIGITS:
+ case UNUM_MULTIPLIER:
+ case UNUM_GROUPING_SIZE:
+ case UNUM_ROUNDING_MODE:
+ case UNUM_FORMAT_WIDTH:
+ case UNUM_PADDING_POSITION:
+ case UNUM_SECONDARY_GROUPING_SIZE:
+ case UNUM_SIGNIFICANT_DIGITS_USED:
+ case UNUM_MIN_SIGNIFICANT_DIGITS:
+ case UNUM_MAX_SIGNIFICANT_DIGITS:
+ case UNUM_LENIENT_PARSE:
+ convert_to_long_ex(value);
+ unum_setAttribute(FORMATTER_OBJECT(nfo), attribute, Z_LVAL_PP(value));
+ break;
+ case UNUM_ROUNDING_INCREMENT:
+ convert_to_double_ex(value);
+ unum_setDoubleAttribute(FORMATTER_OBJECT(nfo), attribute, Z_DVAL_PP(value));
+ break;
+ default:
+ INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
+ break;
+ }
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting attribute value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setTextAttribute( int $attr, string $value )
+ * Get formatter attribute value. }}} */
+/* {{{ proto bool numfmt_set_text_attribute( NumberFormatter $nf, int $attr, string $value )
+ * Get formatter attribute value.
+ */
+PHP_FUNCTION( numfmt_set_text_attribute )
+{
+ int slength = 0;
+ UChar *svalue = NULL;
+ long attribute;
+ char *value;
+ int len;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols",
+ &object, NumberFormatter_ce_ptr, &attribute, &value, &len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_text_attribute: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert given attribute value to UTF-16. */
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting attribute value to UTF-16" );
+
+ /* Actually set new attribute value. */
+ unum_setTextAttribute(FORMATTER_OBJECT(nfo), attribute, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
+ if (svalue) {
+ efree(svalue);
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getSymbol( int $attr )
+ * Get formatter symbol value. }}} */
+/* {{{ proto string numfmt_get_symbol( NumberFormatter $nf, int $attr )
+ * Get formatter symbol value.
+ */
+PHP_FUNCTION( numfmt_get_symbol )
+{
+ long symbol;
+ UChar value_buf[4];
+ UChar *value = value_buf;
+ int length = USIZE(value_buf);
+ 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;
+ }
+
+ if(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "numfmt_get_symbol: invalid symbol value", 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_buf )) {
+ ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting symbol value" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value_buf != value ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setSymbol( int $attr, string $symbol )
+ * Set formatter symbol value. }}} */
+/* {{{ proto bool numfmt_set_symbol( NumberFormatter $nf, int $attr, string $symbol )
+ * Set formatter symbol value.
+ */
+PHP_FUNCTION( numfmt_set_symbol )
+{
+ long symbol;
+ char* value = NULL;
+ int value_len = 0;
+ UChar* svalue = 0;
+ int slength = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ols",
+ &object, NumberFormatter_ce_ptr, &symbol, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_symbol: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if (symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "numfmt_set_symbol: invalid symbol value", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert given symbol to UTF-16. */
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting symbol value to UTF-16" );
+
+ /* Actually set the symbol. */
+ unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
+ if (svalue) {
+ efree(svalue);
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string numfmt_get_pattern( NumberFormatter $nf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( numfmt_get_pattern )
+{
+ UChar value_buf[64];
+ int length = USIZE( value_buf );
+ UChar* value = value_buf;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_pattern: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ length = unum_toPattern(FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo));
+ if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
+ ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
+ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
+ value = eumalloc(length);
+ length = unum_toPattern( FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) );
+ if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ efree(value);
+ value = value_buf;
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting formatter pattern" );
+
+ INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
+}
+/* }}} */
+
+/* {{{ proto bool NumberFormatter::setPattern( string $pattern )
+ * Set formatter pattern. }}} */
+/* {{{ proto bool numfmt_set_pattern( NumberFormatter $nf, string $pattern )
+ * Set formatter pattern.
+ */
+PHP_FUNCTION( numfmt_set_pattern )
+{
+ char* value = NULL;
+ int value_len = 0;
+ int slength = 0;
+ UChar* svalue = NULL;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, NumberFormatter_ce_ptr, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_set_pattern: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert given pattern to UTF-16. */
+ intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error converting pattern to UTF-16" );
+
+ /* TODO: add parse error information */
+ unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (svalue) {
+ efree(svalue);
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Error setting pattern value" );
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getLocale([int type])
+ * Get formatter locale. }}} */
+/* {{{ proto string numfmt_get_locale( NumberFormatter $nf[, int type] )
+ * Get formatter locale.
+ */
+PHP_FUNCTION( numfmt_get_locale )
+{
+ long type = ULOC_ACTUAL_LOCALE;
+ char* loc;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l",
+ &object, NumberFormatter_ce_ptr, &type ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ loc = (char *)unum_getLocaleByType(FORMATTER_OBJECT(nfo), type, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Error getting locale" );
+ RETURN_STRING(loc, 1);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/formatter/formatter_attr.h b/ext/intl/formatter/formatter_attr.h
new file mode 100644
index 0000000..c4bc94c
--- /dev/null
+++ b/ext/intl/formatter/formatter_attr.h
@@ -0,0 +1,32 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_ATTR_H
+#define FORMATTER_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_set_attribute );
+PHP_FUNCTION( numfmt_get_attribute );
+PHP_FUNCTION( numfmt_set_text_attribute );
+PHP_FUNCTION( numfmt_get_text_attribute );
+PHP_FUNCTION( numfmt_set_symbol );
+PHP_FUNCTION( numfmt_get_symbol );
+PHP_FUNCTION( numfmt_set_pattern );
+PHP_FUNCTION( numfmt_get_pattern );
+PHP_FUNCTION( numfmt_get_locale );
+
+#endif // FORMATTER_ATTR_H
diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c
new file mode 100644
index 0000000..2246cd2
--- /dev/null
+++ b/ext/intl/formatter/formatter_class.c
@@ -0,0 +1,226 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+#include <zend_exceptions.h>
+
+zend_class_entry *NumberFormatter_ce_ptr = NULL;
+static zend_object_handlers NumberFormatter_handlers;
+
+/*
+ * 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 );
+ object_properties_init(&intern->zo, ce);
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ NumberFormatter_object_dtor,
+ (zend_objects_free_object_storage_t)NumberFormatter_object_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = &NumberFormatter_handlers;
+
+ return retval;
+}
+/* }}} */
+
+/* {{{ NumberFormatter_object_clone */
+zend_object_value NumberFormatter_object_clone(zval *object TSRMLS_DC)
+{
+ zend_object_value new_obj_val;
+ zend_object_handle handle = Z_OBJ_HANDLE_P(object);
+ NumberFormatter_object *nfo, *new_nfo;
+
+ FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK;
+ new_obj_val = NumberFormatter_ce_ptr->create_object(Z_OBJCE_P(object) TSRMLS_CC);
+ new_nfo = (NumberFormatter_object *)zend_object_store_get_object_by_handle(new_obj_val.handle TSRMLS_CC);
+ /* clone standard parts */
+ zend_objects_clone_members(&new_nfo->zo, new_obj_val, &nfo->zo, handle TSRMLS_CC);
+ /* clone formatter object. It may fail, the destruction code must handle this case */
+ if (FORMATTER_OBJECT(nfo) != NULL) {
+ FORMATTER_OBJECT(new_nfo) = unum_clone(FORMATTER_OBJECT(nfo),
+ &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
+ /* set up error in case error handler is interested */
+ intl_errors_set(INTL_DATA_ERROR_P(nfo), INTL_DATA_ERROR_CODE(nfo),
+ "Failed to clone NumberFormatter object", 0 TSRMLS_CC);
+ zend_throw_exception(NULL, "Failed to clone NumberFormatter object", 0 TSRMLS_CC);
+ }
+ } else {
+ zend_throw_exception(NULL, "Cannot clone unconstructed NumberFormatter", 0 TSRMLS_CC);
+ }
+ return new_obj_val;
+}
+/* }}} */
+
+/*
+ * 'NumberFormatter' class registration structures & functions
+ */
+
+/* {{{ arginfo */
+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()
+
+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()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_getattribute, 0, 0, 1)
+ ZEND_ARG_INFO(0, attr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_setattribute, 0, 0, 2)
+ ZEND_ARG_INFO(0, attr)
+ ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_setsymbol, 0, 0, 2)
+ ZEND_ARG_INFO(0, attr)
+ ZEND_ARG_INFO(0, symbol)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_numberformatter_getpattern, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_setpattern, 0, 0, 1)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_getlocale, 0, 0, 0)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter___construct, 0, 0, 2)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, style)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_formatcurrency, 0, 0, 2)
+ ZEND_ARG_INFO(0, num)
+ ZEND_ARG_INFO(0, currency)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numberformatter_format, 0, 0, 1)
+ ZEND_ARG_INFO(0, num)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ NumberFormatter_class_functions
+ * Every 'NumberFormatter' class method has an entry in this table
+ */
+static zend_function_entry NumberFormatter_class_functions[] = {
+ PHP_ME( NumberFormatter, __construct, arginfo_numberformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( numfmt_create ), arginfo_numberformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( format, ZEND_FN( numfmt_format ), arginfo_numberformatter_format )
+ PHP_NAMED_FE( parse, ZEND_FN( numfmt_parse ), number_parse_arginfo )
+ PHP_NAMED_FE( formatCurrency, ZEND_FN( numfmt_format_currency ), arginfo_numberformatter_formatcurrency )
+ PHP_NAMED_FE( parseCurrency, ZEND_FN( numfmt_parse_currency ), number_parse_currency_arginfo )
+ PHP_NAMED_FE( setAttribute, ZEND_FN( numfmt_set_attribute ), arginfo_numberformatter_setattribute )
+ PHP_NAMED_FE( getAttribute, ZEND_FN( numfmt_get_attribute ), arginfo_numberformatter_getattribute )
+ PHP_NAMED_FE( setTextAttribute, ZEND_FN( numfmt_set_text_attribute ), arginfo_numberformatter_setattribute )
+ PHP_NAMED_FE( getTextAttribute, ZEND_FN( numfmt_get_text_attribute ), arginfo_numberformatter_getattribute )
+ PHP_NAMED_FE( setSymbol, ZEND_FN( numfmt_set_symbol ), arginfo_numberformatter_setsymbol )
+ PHP_NAMED_FE( getSymbol, ZEND_FN( numfmt_get_symbol ), arginfo_numberformatter_getattribute )
+ PHP_NAMED_FE( setPattern, ZEND_FN( numfmt_set_pattern ), arginfo_numberformatter_setpattern )
+ PHP_NAMED_FE( getPattern, ZEND_FN( numfmt_get_pattern ), arginfo_numberformatter_getpattern )
+ PHP_NAMED_FE( getLocale, ZEND_FN( numfmt_get_locale ), arginfo_numberformatter_getlocale )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( numfmt_get_error_code ), arginfo_numberformatter_getpattern )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( numfmt_get_error_message ), arginfo_numberformatter_getpattern )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 );
+
+ memcpy(&NumberFormatter_handlers, zend_get_std_object_handlers(),
+ sizeof(NumberFormatter_handlers));
+ NumberFormatter_handlers.clone_obj = NumberFormatter_object_clone;
+
+ /* 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 100644
index 0000000..9582866
--- /dev/null
+++ b/ext/intl/formatter/formatter_class.h
@@ -0,0 +1,50 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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_OBJECT(nfo) (nfo)->nf_data.unum
+#define FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(NumberFormatter, nfo)
+#define FORMATTER_METHOD_FETCH_OBJECT \
+ FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK; \
+ if (FORMATTER_OBJECT(nfo) == NULL) \
+ { \
+ intl_errors_set(&nfo->nf_data.error, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Found unconstructed NumberFormatter", 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ }
+
+
+#endif // #ifndef FORMATTER_CLASS_H
diff --git a/ext/intl/formatter/formatter_data.c b/ext/intl/formatter/formatter_data.c
new file mode 100644
index 0000000..88f122f
--- /dev/null
+++ b/ext/intl/formatter/formatter_data.c
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "formatter_data.h"
+
+/* {{{ void formatter_data_init( formatter_data* nf_data )
+ * Initialize internals of formatter_data.
+ */
+void formatter_data_init( formatter_data* nf_data TSRMLS_DC )
+{
+ if( !nf_data )
+ return;
+
+ nf_data->unum = NULL;
+ intl_error_reset( &nf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void formatter_data_free( formatter_data* nf_data )
+ * Clean up mem allocted by internals of formatter_data
+ */
+void formatter_data_free( formatter_data* nf_data TSRMLS_DC )
+{
+ if( !nf_data )
+ return;
+
+ if( nf_data->unum )
+ unum_close( nf_data->unum );
+
+ nf_data->unum = NULL;
+ intl_error_reset( &nf_data->error TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ formatter_data* formatter_data_create()
+ * Alloc mem for formatter_data and initialize it with default values.
+ */
+formatter_data* formatter_data_create( TSRMLS_D )
+{
+ formatter_data* nf_data = ecalloc( 1, sizeof(formatter_data) );
+
+ formatter_data_init( nf_data TSRMLS_CC );
+
+ return nf_data;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/formatter/formatter_data.h b/ext/intl/formatter/formatter_data.h
new file mode 100644
index 0000000..adc4818
--- /dev/null
+++ b/ext/intl/formatter/formatter_data.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_DATA_H
+#define FORMATTER_DATA_H
+
+#include <php.h>
+
+#include <unicode/unum.h>
+
+#include "intl_error.h"
+
+typedef struct {
+ // error hangling
+ intl_error error;
+
+ // formatter handling
+ UNumberFormat* unum;
+} formatter_data;
+
+formatter_data* formatter_data_create( TSRMLS_D );
+void formatter_data_init( formatter_data* nf_data TSRMLS_DC );
+void formatter_data_free( formatter_data* nf_data TSRMLS_DC );
+
+#endif // FORMATTER_DATA_H
diff --git a/ext/intl/formatter/formatter_format.c b/ext/intl/formatter/formatter_format.c
new file mode 100644
index 0000000..ce1c941
--- /dev/null
+++ b/ext/intl/formatter/formatter_format.c
@@ -0,0 +1,204 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+#include "intl_convert.h"
+
+/* {{{ proto mixed NumberFormatter::format( mixed $num[, int $type] )
+ * Format a number. }}} */
+/* {{{ proto mixed numfmt_format( NumberFormatter $nf, mixed $num[, int type] )
+ * Format a number.
+ */
+PHP_FUNCTION( numfmt_format )
+{
+ zval **number;
+ long type = FORMAT_TYPE_DEFAULT;
+ UChar format_buf[32];
+ UChar* formatted = format_buf;
+ int formatted_len = USIZE(format_buf);
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OZ|l",
+ &object, NumberFormatter_ce_ptr, &number, &type ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_format: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ if(type == FORMAT_TYPE_DEFAULT) {
+ if(Z_TYPE_PP(number) == IS_STRING) {
+ convert_scalar_to_number_ex(number);
+ }
+
+ if(Z_TYPE_PP(number) == IS_LONG) {
+ /* take INT32 on 32-bit, int64 on 64-bit */
+ type = (sizeof(long) == 8)?FORMAT_TYPE_INT64:FORMAT_TYPE_INT32;
+ } else if(Z_TYPE_PP(number) == IS_DOUBLE) {
+ type = FORMAT_TYPE_DOUBLE;
+ } else {
+ type = FORMAT_TYPE_INT32;
+ }
+ }
+
+ if(Z_TYPE_PP(number) != IS_DOUBLE && Z_TYPE_PP(number) != IS_LONG) {
+ SEPARATE_ZVAL_IF_NOT_REF(number);
+ convert_scalar_to_number( *number TSRMLS_CC );
+ }
+
+ switch(type) {
+ case FORMAT_TYPE_INT32:
+ convert_to_long_ex(number);
+ formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_PP(number),
+ formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_PP(number),
+ formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ break;
+
+ case FORMAT_TYPE_INT64:
+ {
+ int64_t value = (Z_TYPE_PP(number) == IS_DOUBLE)?(int64_t)Z_DVAL_PP(number):Z_LVAL_PP(number);
+ formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ }
+ break;
+
+ case FORMAT_TYPE_DOUBLE:
+ convert_to_double_ex(number);
+ formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_PP(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_PP(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
+ efree(formatted);
+ }
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
+ break;
+
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported format type %ld", type);
+ RETURN_FALSE;
+ break;
+ }
+
+ INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+}
+/* }}} */
+
+/* {{{ proto mixed NumberFormatter::formatCurrency( double $num, string $currency )
+ * Format a number as currency. }}} */
+/* {{{ proto mixed numfmt_format_currency( NumberFormatter $nf, double $num, string $currency )
+ * Format a number as currency.
+ */
+PHP_FUNCTION( numfmt_format_currency )
+{
+ double number;
+ UChar format_buf[32];
+ UChar* formatted = format_buf;
+ int formatted_len = USIZE(format_buf);
+ char* currency = NULL;
+ int currency_len = 0;
+ UChar* scurrency = NULL;
+ int scurrency_len = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ods",
+ &object, NumberFormatter_ce_ptr, &number, &currency, &currency_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_format_currency: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert currency to UTF-16. */
+ intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-16 failed" );
+
+ /* Format the number using a fixed-length buffer. */
+ formatted_len = unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+ /* If the buffer turned out to be too small
+ * then allocate another buffer dynamically
+ * and use it to format the number.
+ */
+ if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
+ intl_error_reset(INTL_DATA_ERROR_P(nfo) TSRMLS_CC);
+ formatted = eumalloc(formatted_len);
+ unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
+ }
+
+ if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) {
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((nfo)) TSRMLS_CC );
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P(nfo), "Number formatting failed", 0 TSRMLS_CC );
+ RETVAL_FALSE;
+ if (formatted != format_buf) {
+ efree(formatted);
+ }
+ } else {
+ INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
+ }
+
+ if(scurrency) {
+ efree(scurrency);
+ }
+}
+
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/formatter/formatter_format.h b/ext/intl/formatter/formatter_format.h
new file mode 100644
index 0000000..35fafd1
--- /dev/null
+++ b/ext/intl/formatter/formatter_format.h
@@ -0,0 +1,31 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_FORMAT_H
+#define FORMATTER_FORMAT_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_format );
+PHP_FUNCTION( numfmt_format_currency );
+
+#define FORMAT_TYPE_DEFAULT 0
+#define FORMAT_TYPE_INT32 1
+#define FORMAT_TYPE_INT64 2
+#define FORMAT_TYPE_DOUBLE 3
+#define FORMAT_TYPE_CURRENCY 4
+
+#endif // FORMATTER_FORMAT_H
diff --git a/ext/intl/formatter/formatter_main.c b/ext/intl/formatter/formatter_main.c
new file mode 100644
index 0000000..5cb6483
--- /dev/null
+++ b/ext/intl/formatter/formatter_main.c
@@ -0,0 +1,156 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "intl_convert.h"
+
+/* {{{ */
+static void numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
+{
+ char* locale;
+ char* pattern = NULL;
+ int locale_len = 0, pattern_len = 0;
+ long style;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sl|s",
+ &locale, &locale_len, &style, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_create: unable to parse input parameters", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value);
+ object = return_value;
+ FORMATTER_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ /* Convert pattern (if specified) to UTF-16. */
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: error converting pattern to UTF-16");
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+ /* Create an ICU number formatter. */
+ FORMATTER_OBJECT(nfo) = unum_open(style, spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(nfo));
+
+ if(spattern) {
+ efree(spattern);
+ }
+
+ INTL_CTOR_CHECK_STATUS(nfo, "numfmt_create: number formatter creation failed");
+}
+/* }}} */
+
+/* {{{ proto NumberFormatter NumberFormatter::create( string $locale, int style[, string $pattern ] )
+ * Create number formatter. }}} */
+/* {{{ proto NumberFormatter numfmt_create( string $locale, int style[, string $pattern ] )
+ * Create number formatter.
+ */
+PHP_FUNCTION( numfmt_create )
+{
+ object_init_ex( return_value, NumberFormatter_ce_ptr );
+ numfmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto void NumberFormatter::__construct( string $locale, int style[, string $pattern ] )
+ * NumberFormatter object constructor.
+ */
+PHP_METHOD( NumberFormatter, __construct )
+{
+ return_value = getThis();
+ numfmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int NumberFormatter::getErrorCode()
+ * Get formatter's last error code. }}} */
+/* {{{ proto int numfmt_get_error_code( NumberFormatter $nf )
+ * Get formatter's last error code.
+ */
+PHP_FUNCTION( numfmt_get_error_code )
+{
+ FORMATTER_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ nfo = (NumberFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ /* Return formatter's last error code. */
+ RETURN_LONG( INTL_DATA_ERROR_CODE(nfo) );
+}
+/* }}} */
+
+/* {{{ proto string NumberFormatter::getErrorMessage( )
+ * Get text description for formatter's last error code. }}} */
+/* {{{ proto string numfmt_get_error_message( NumberFormatter $nf )
+ * Get text description for formatter's last error code.
+ */
+PHP_FUNCTION( numfmt_get_error_message )
+{
+ char* message = NULL;
+ FORMATTER_METHOD_INIT_VARS
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, NumberFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "numfmt_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ nfo = (NumberFormatter_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ /* Return last error message. */
+ message = intl_error_get_message( INTL_DATA_ERROR_P(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 100644
index 0000000..7669e68
--- /dev/null
+++ b/ext/intl/formatter/formatter_main.h
@@ -0,0 +1,27 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_MAIN_H
+#define FORMATTER_MAIN_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_create );
+PHP_FUNCTION( numfmt_get_error_code );
+PHP_FUNCTION( numfmt_get_error_message );
+PHP_METHOD( NumberFormatter, __construct );
+
+#endif // FORMATTER_FORMAT_H
diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c
new file mode 100644
index 0000000..6f3a3a1
--- /dev/null
+++ b/ext/intl/formatter/formatter_parse.c
@@ -0,0 +1,187 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 <locale.h>
+
+#include "php_intl.h"
+#include "formatter_class.h"
+#include "formatter_format.h"
+#include "formatter_parse.h"
+#include "intl_convert.h"
+
+#define ICU_LOCALE_BUG 1
+
+/* {{{ proto mixed NumberFormatter::parse( string $str[, int $type, int &$position ])
+ * Parse a number. }}} */
+/* {{{ proto mixed numfmt_parse( NumberFormatter $nf, string $str[, int $type, int &$position ])
+ * Parse a number.
+ */
+PHP_FUNCTION( numfmt_parse )
+{
+ long type = FORMAT_TYPE_DOUBLE;
+ UChar* sstr = NULL;
+ int sstr_len = 0;
+ char* str = NULL;
+ int str_len;
+ int32_t val32, position = 0;
+ int64_t val64;
+ double val_double;
+ int32_t* position_p = NULL;
+ zval *zposition = NULL;
+ char *oldlocale;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|lz!",
+ &object, NumberFormatter_ce_ptr, &str, &str_len, &type, &zposition ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "number_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert given string to UTF-16. */
+ intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
+
+ if(zposition) {
+ convert_to_long(zposition);
+ position = (int32_t)Z_LVAL_P( zposition );
+ position_p = &position;
+ }
+
+#if ICU_LOCALE_BUG && defined(LC_NUMERIC)
+ oldlocale = setlocale(LC_NUMERIC, "C");
+#endif
+
+ switch(type) {
+ case FORMAT_TYPE_INT32:
+ val32 = unum_parse(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ RETVAL_LONG(val32);
+ break;
+ case FORMAT_TYPE_INT64:
+ val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ if(val64 > LONG_MAX || val64 < LONG_MIN) {
+ RETVAL_DOUBLE(val64);
+ } else {
+ RETVAL_LONG((long)val64);
+ }
+ break;
+ case FORMAT_TYPE_DOUBLE:
+ val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
+ RETVAL_DOUBLE(val_double);
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported format type %ld", type);
+ RETVAL_FALSE;
+ break;
+ }
+#if ICU_LOCALE_BUG && defined(LC_NUMERIC)
+ setlocale(LC_NUMERIC, oldlocale);
+#endif
+ if(zposition) {
+ zval_dtor(zposition);
+ ZVAL_LONG(zposition, position);
+ }
+
+ if (sstr) {
+ efree(sstr);
+ }
+
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+}
+/* }}} */
+
+/* {{{ proto double NumberFormatter::parseCurrency( string $str, string $&currency[, int $&position] )
+ * Parse a number as currency. }}} */
+/* {{{ proto double numfmt_parse_currency( NumberFormatter $nf, string $str, string $&currency[, int $&position] )
+ * Parse a number as currency.
+ */
+PHP_FUNCTION( numfmt_parse_currency )
+{
+ double number;
+ UChar currency[5] = {0};
+ UChar* sstr = NULL;
+ int sstr_len = 0;
+ char *currency_str = NULL;
+ int currency_len = 0;
+ char *str;
+ int str_len;
+ int32_t* position_p = NULL;
+ int32_t position = 0;
+ zval *zcurrency, *zposition = NULL;
+ FORMATTER_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Osz|z!",
+ &object, NumberFormatter_ce_ptr, &str, &str_len, &zcurrency, &zposition ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "number_parse_currency: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ FORMATTER_METHOD_FETCH_OBJECT;
+
+ /* Convert given string to UTF-16. */
+ intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
+
+ if(zposition) {
+ convert_to_long(zposition);
+ position = (int32_t)Z_LVAL_P( zposition );
+ position_p = &position;
+ }
+
+ number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
+ if(zposition) {
+ zval_dtor(zposition);
+ ZVAL_LONG(zposition, position);
+ }
+ if (sstr) {
+ efree(sstr);
+ }
+ INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
+
+ /* Convert parsed currency to UTF-8 and pass it back to caller. */
+ intl_convert_utf16_to_utf8(&currency_str, &currency_len, currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo));
+ INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" );
+ zval_dtor( zcurrency );
+ ZVAL_STRINGL(zcurrency, currency_str, currency_len, 0);
+
+ RETVAL_DOUBLE( number );
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/formatter/formatter_parse.h b/ext/intl/formatter/formatter_parse.h
new file mode 100644
index 0000000..cb96c72
--- /dev/null
+++ b/ext/intl/formatter/formatter_parse.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef FORMATTER_PARSE_H
+#define FORMATTER_PARSE_H
+
+#include <php.h>
+
+PHP_FUNCTION( numfmt_parse );
+PHP_FUNCTION( numfmt_parse_currency );
+
+#endif // FORMATTER_PARSE_H
diff --git a/ext/intl/grapheme/grapheme.h b/ext/intl/grapheme/grapheme.h
new file mode 100644
index 0000000..c0e697a
--- /dev/null
+++ b/ext/intl/grapheme/grapheme.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef GRAPHEME_GRAPHEME_H
+#define GRAPHEME_GRAPHEME_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+#include <unicode/ubrk.h>
+
+PHP_FUNCTION(grapheme_strlen);
+PHP_FUNCTION(grapheme_strpos);
+PHP_FUNCTION(grapheme_stripos);
+PHP_FUNCTION(grapheme_strrpos);
+PHP_FUNCTION(grapheme_strripos);
+PHP_FUNCTION(grapheme_substr);
+PHP_FUNCTION(grapheme_strstr);
+PHP_FUNCTION(grapheme_stristr);
+PHP_FUNCTION(grapheme_extract);
+
+void grapheme_register_constants( INIT_FUNC_ARGS );
+void grapheme_close_global_iterator( TSRMLS_D );
+
+#endif // GRAPHEME_GRAPHEME_H
diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c
new file mode 100644
index 0000000..475bbe4
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_string.c
@@ -0,0 +1,939 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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", 0 TSRMLS_CC );
+ if (ustring) {
+ efree( ustring );
+ }
+ RETURN_NULL();
+ }
+
+ ret_len = grapheme_split_string(ustring, ustring_len, NULL, 0 TSRMLS_CC );
+
+ if (ustring) {
+ 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", 0 TSRMLS_CC );
+ if (ustr) {
+ 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 );
+
+ if (ustr) {
+ 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);
+
+ if (ustr) {
+ 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", 0 TSRMLS_CC );
+
+ if (sub_str) {
+ 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) {
+ if(length < 0) {
+
+ 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;
+ } else {
+ sub_str_end_pos = ustr_len;
+ }
+ }
+
+ 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", 0 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 */
+static 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 */
+static 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 */
+static 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_dtor(next);
+ ZVAL_LONG(next, lstart);
+ }
+ }
+
+ 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", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ if ( size > INT32_MAX || size < 0) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extract: size is invalid", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ if (size == 0) {
+ RETURN_EMPTY_STRING();
+ }
+
+ /* 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 ) ) {
+ long nsize = ( size < str_len ? size : str_len );
+ if ( NULL != next ) {
+ ZVAL_LONG(next, start+nsize);
+ }
+ RETURN_STRINGL(((char *)pstr), nsize, 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", 0 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);
+
+ if (ustr) {
+ 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 100644
index 0000000..9200855
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_util.c
@@ -0,0 +1,642 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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", 0 TSRMLS_CC );
+ if (uhaystack) {
+ 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 );
+ if (uhaystack) {
+ 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", 0 TSRMLS_CC );
+ if (uhaystack) {
+ efree( uhaystack );
+ }
+ if (uneedle) {
+ 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:
+ if (uhaystack) {
+ efree( uhaystack );
+ }
+ if (uneedle) {
+ 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", 0 TSRMLS_CC );
+ if (uhaystack) {
+ 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 );
+ if (uhaystack) {
+ 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", 0 TSRMLS_CC );
+ if (uhaystack) {
+ efree( uhaystack );
+ }
+ if (uneedle) {
+ 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);
+
+ if (uhaystack) {
+ efree( uhaystack );
+ }
+ if (uneedle) {
+ 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 */
+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 */
+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 100644
index 0000000..c91aeaf
--- /dev/null
+++ b/ext/intl/grapheme/grapheme_util.h
@@ -0,0 +1,59 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef GRAPHEME_GRAPHEME_UTIL_H
+#define GRAPHEME_GRAPHEME_UTIL_H
+
+#include "php_intl.h"
+#include "intl_convert.h"
+
+/* get_break_interator: get a break iterator from the global structure */
+UBreakIterator* grapheme_get_break_iterator(void *stack_buffer, UErrorCode *status TSRMLS_DC );
+
+void
+grapheme_substr_ascii(char *str, int32_t str_len, int32_t f, int32_t l, int argc, char **sub_str, int *sub_str_len);
+
+int
+grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int f_ignore_case TSRMLS_DC);
+
+int
+grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned char*needle, int32_t needle_len, int32_t offset, int *puchar_pos, int f_ignore_case TSRMLS_DC);
+
+int grapheme_ascii_check(const unsigned char *day, int32_t len);
+
+int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_array[], int boundary_array_len TSRMLS_DC );
+
+int32_t
+grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len);
+
+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/idn/idn.c b/ext/intl/idn/idn.c
new file mode 100644
index 0000000..6332488
--- /dev/null
+++ b/ext/intl/idn/idn.c
@@ -0,0 +1,351 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2009 The PHP Group |
+ +----------------------------------------------------------------------+
+ | 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: Pierre A. Joye <pierre@php.net> |
+ | Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+/* {{{ includes */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+
+#include <unicode/uidna.h>
+#include <unicode/ustring.h>
+#include "ext/standard/php_string.h"
+
+#include "intl_error.h"
+#include "intl_convert.h"
+/* }}} */
+
+#ifdef UIDNA_INFO_INITIALIZER
+#define HAVE_46_API 1 /* has UTS#46 API (introduced in ICU 4.6) */
+#endif
+
+enum {
+ INTL_IDN_VARIANT_2003 = 0,
+ INTL_IDN_VARIANT_UTS46
+};
+
+/* {{{ grapheme_register_constants
+ * Register API constants
+ */
+void idn_register_constants( INIT_FUNC_ARGS )
+{
+ /* OPTIONS */
+
+ /* Option to prohibit processing of unassigned codepoints in the input and
+ do not check if the input conforms to STD-3 ASCII rules. */
+ REGISTER_LONG_CONSTANT("IDNA_DEFAULT", UIDNA_DEFAULT, CONST_CS | CONST_PERSISTENT);
+
+ /* Option to allow processing of unassigned codepoints in the input */
+ REGISTER_LONG_CONSTANT("IDNA_ALLOW_UNASSIGNED", UIDNA_ALLOW_UNASSIGNED, CONST_CS | CONST_PERSISTENT);
+
+ /* Option to check if input conforms to STD-3 ASCII rules */
+ REGISTER_LONG_CONSTANT("IDNA_USE_STD3_RULES", UIDNA_USE_STD3_RULES, CONST_CS | CONST_PERSISTENT);
+
+#ifdef HAVE_46_API
+
+ /* Option to check for whether the input conforms to the BiDi rules.
+ * Ignored by the IDNA2003 implementation. (IDNA2003 always performs a BiDi check.) */
+ REGISTER_LONG_CONSTANT("IDNA_CHECK_BIDI", UIDNA_CHECK_BIDI, CONST_CS | CONST_PERSISTENT);
+
+ /* Option to check for whether the input conforms to the CONTEXTJ rules.
+ * Ignored by the IDNA2003 implementation. (The CONTEXTJ check is new in IDNA2008.) */
+ REGISTER_LONG_CONSTANT("IDNA_CHECK_CONTEXTJ", UIDNA_CHECK_CONTEXTJ, CONST_CS | CONST_PERSISTENT);
+
+ /* Option for nontransitional processing in ToASCII().
+ * By default, ToASCII() uses transitional processing.
+ * Ignored by the IDNA2003 implementation. */
+ REGISTER_LONG_CONSTANT("IDNA_NONTRANSITIONAL_TO_ASCII", UIDNA_NONTRANSITIONAL_TO_ASCII, CONST_CS | CONST_PERSISTENT);
+
+ /* Option for nontransitional processing in ToUnicode().
+ * By default, ToUnicode() uses transitional processing.
+ * Ignored by the IDNA2003 implementation. */
+ REGISTER_LONG_CONSTANT("IDNA_NONTRANSITIONAL_TO_UNICODE", UIDNA_NONTRANSITIONAL_TO_UNICODE, CONST_CS | CONST_PERSISTENT);
+#endif
+
+ /* VARIANTS */
+ REGISTER_LONG_CONSTANT("INTL_IDNA_VARIANT_2003", INTL_IDN_VARIANT_2003, CONST_CS | CONST_PERSISTENT);
+#ifdef HAVE_46_API
+ REGISTER_LONG_CONSTANT("INTL_IDNA_VARIANT_UTS46", INTL_IDN_VARIANT_UTS46, CONST_CS | CONST_PERSISTENT);
+#endif
+
+#ifdef HAVE_46_API
+ /* PINFO ERROR CODES */
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_EMPTY_LABEL", UIDNA_ERROR_EMPTY_LABEL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_LABEL_TOO_LONG", UIDNA_ERROR_LABEL_TOO_LONG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_DOMAIN_NAME_TOO_LONG", UIDNA_ERROR_DOMAIN_NAME_TOO_LONG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_LEADING_HYPHEN", UIDNA_ERROR_LEADING_HYPHEN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_TRAILING_HYPHEN", UIDNA_ERROR_TRAILING_HYPHEN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_HYPHEN_3_4", UIDNA_ERROR_HYPHEN_3_4, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_LEADING_COMBINING_MARK", UIDNA_ERROR_LEADING_COMBINING_MARK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_DISALLOWED", UIDNA_ERROR_DISALLOWED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_PUNYCODE", UIDNA_ERROR_PUNYCODE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_LABEL_HAS_DOT", UIDNA_ERROR_LABEL_HAS_DOT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_INVALID_ACE_LABEL", UIDNA_ERROR_INVALID_ACE_LABEL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_BIDI", UIDNA_ERROR_BIDI, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IDNA_ERROR_CONTEXTJ", UIDNA_ERROR_CONTEXTJ, CONST_CS | CONST_PERSISTENT);
+#endif
+}
+/* }}} */
+
+enum {
+ INTL_IDN_TO_ASCII = 0,
+ INTL_IDN_TO_UTF8
+};
+
+/* like INTL_CHECK_STATUS, but as a function and varying the name of the func */
+static int php_intl_idn_check_status(UErrorCode err, const char *msg, int mode TSRMLS_DC)
+{
+ intl_error_set_code(NULL, err TSRMLS_CC);
+ if (U_FAILURE(err)) {
+ char *buff;
+ spprintf(&buff, 0, "%s: %s",
+ mode == INTL_IDN_TO_ASCII ? "idn_to_ascii" : "idn_to_utf8",
+ msg);
+ intl_error_set_custom_msg(NULL, buff, 1 TSRMLS_CC);
+ efree(buff);
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static inline void php_intl_bad_args(const char *msg, int mode TSRMLS_DC)
+{
+ php_intl_idn_check_status(U_ILLEGAL_ARGUMENT_ERROR, msg, mode TSRMLS_CC);
+}
+
+#ifdef HAVE_46_API
+static void php_intl_idn_to_46(INTERNAL_FUNCTION_PARAMETERS,
+ const char *domain, int domain_len, uint32_t option, int mode, zval *idna_info)
+{
+ UErrorCode status = U_ZERO_ERROR;
+ UIDNA *uts46;
+ int32_t len;
+ int32_t buffer_capac = 255; /* no domain name may exceed this */
+ char *buffer = emalloc(buffer_capac);
+ UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+ int buffer_used = 0;
+
+ uts46 = uidna_openUTS46(option, &status);
+ if (php_intl_idn_check_status(status, "failed to open UIDNA instance",
+ mode TSRMLS_CC) == FAILURE) {
+ efree(buffer);
+ RETURN_FALSE;
+ }
+
+ if (mode == INTL_IDN_TO_ASCII) {
+ len = uidna_nameToASCII_UTF8(uts46, domain, (int32_t)domain_len,
+ buffer, buffer_capac, &info, &status);
+ } else {
+ len = uidna_nameToUnicodeUTF8(uts46, domain, (int32_t)domain_len,
+ buffer, buffer_capac, &info, &status);
+ }
+ if (php_intl_idn_check_status(status, "failed to convert name",
+ mode TSRMLS_CC) == FAILURE) {
+ uidna_close(uts46);
+ efree(buffer);
+ RETURN_FALSE;
+ }
+ if (len >= 255) {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "ICU returned an unexpected length");
+ }
+
+ buffer[len] = '\0';
+
+ if (info.errors == 0) {
+ RETVAL_STRINGL(buffer, len, 0);
+ buffer_used = 1;
+ } else {
+ RETVAL_FALSE;
+ }
+
+ if (idna_info) {
+ if (buffer_used) { /* used in return_value then */
+ zval_addref_p(return_value);
+ add_assoc_zval_ex(idna_info, "result", sizeof("result"), return_value);
+ } else {
+ zval *zv;
+ ALLOC_INIT_ZVAL(zv);
+ ZVAL_STRINGL(zv, buffer, len, 0);
+ buffer_used = 1;
+ add_assoc_zval_ex(idna_info, "result", sizeof("result"), zv);
+ }
+ add_assoc_bool_ex(idna_info, "isTransitionalDifferent",
+ sizeof("isTransitionalDifferent"), info.isTransitionalDifferent);
+ add_assoc_long_ex(idna_info, "errors", sizeof("errors"), (long)info.errors);
+ }
+
+ if (!buffer_used) {
+ efree(buffer);
+ }
+
+ uidna_close(uts46);
+}
+#endif
+
+static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS,
+ const char *domain, int domain_len, uint32_t option, int mode)
+{
+ UChar* ustring = NULL;
+ int ustring_len = 0;
+ UErrorCode status;
+ char *converted_utf8;
+ int32_t converted_utf8_len;
+ UChar converted[MAXPATHLEN];
+ int32_t converted_ret_len;
+
+ /* convert the string to UTF-16. */
+ status = U_ZERO_ERROR;
+ intl_convert_utf8_to_utf16(&ustring, &ustring_len, domain, domain_len, &status);
+
+ if (U_FAILURE(status)) {
+ intl_error_set_code(NULL, status TSRMLS_CC);
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
+ if (ustring) {
+ efree(ustring);
+ }
+ RETURN_FALSE;
+ } else {
+ UParseError parse_error;
+
+ status = U_ZERO_ERROR;
+ if (mode == INTL_IDN_TO_ASCII) {
+ converted_ret_len = uidna_IDNToASCII(ustring, ustring_len, converted, MAXPATHLEN, (int32_t)option, &parse_error, &status);
+ } else {
+ converted_ret_len = uidna_IDNToUnicode(ustring, ustring_len, converted, MAXPATHLEN, (int32_t)option, &parse_error, &status);
+ }
+ efree(ustring);
+
+ if (U_FAILURE(status)) {
+ intl_error_set( NULL, status, "idn_to_ascii: cannot convert to ASCII", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ status = U_ZERO_ERROR;
+ intl_convert_utf16_to_utf8(&converted_utf8, &converted_utf8_len, converted, converted_ret_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 output string to UTF-8", 0 TSRMLS_CC );
+ efree(converted_utf8);
+ RETURN_FALSE;
+ }
+ }
+
+ /* return the allocated string, not a duplicate */
+ RETURN_STRINGL(((char *)converted_utf8), converted_utf8_len, 0);
+}
+
+static void php_intl_idn_handoff(INTERNAL_FUNCTION_PARAMETERS, int mode)
+{
+ char *domain;
+ int domain_len;
+ long option = 0,
+ variant = INTL_IDN_VARIANT_2003;
+ zval *idna_info = NULL;
+
+ intl_error_reset(NULL TSRMLS_CC);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|llz",
+ &domain, &domain_len, &option, &variant, &idna_info) == FAILURE) {
+ php_intl_bad_args("bad arguments", mode TSRMLS_CC);
+ RETURN_NULL(); /* don't set FALSE because that's not the way it was before... */
+ }
+
+#ifdef HAVE_46_API
+ if (variant != INTL_IDN_VARIANT_2003 && variant != INTL_IDN_VARIANT_UTS46) {
+ php_intl_bad_args("invalid variant, must be one of {"
+ "INTL_IDNA_VARIANT_2003, INTL_IDNA_VARIANT_UTS46}", mode TSRMLS_CC);
+ RETURN_FALSE;
+ }
+#else
+ if (variant != INTL_IDN_VARIANT_2003) {
+ php_intl_bad_args("invalid variant, PHP was compiled against "
+ "an old version of ICU and only supports INTL_IDN_VARIANT_2003",
+ mode TSRMLS_CC);
+ RETURN_FALSE;
+ }
+#endif
+
+ if (domain_len < 1) {
+ php_intl_bad_args("empty domain name", mode TSRMLS_CC);
+ RETURN_FALSE;
+ }
+ if (domain_len > INT32_MAX - 1) {
+ php_intl_bad_args("domain name too large", mode TSRMLS_CC);
+ RETURN_FALSE;
+ }
+ /* don't check options; it wasn't checked before */
+
+ if (idna_info != NULL) {
+ if (variant == INTL_IDN_VARIANT_2003) {
+ php_error_docref0(NULL TSRMLS_CC, E_NOTICE,
+ "4 arguments were provided, but INTL_IDNA_VARIANT_2003 only "
+ "takes 3 - extra argument ignored");
+ } else {
+ zval_dtor(idna_info);
+ array_init(idna_info);
+ }
+ }
+
+ if (variant == INTL_IDN_VARIANT_2003) {
+ php_intl_idn_to(INTERNAL_FUNCTION_PARAM_PASSTHRU,
+ domain, domain_len, (uint32_t)option, mode);
+ }
+#ifdef HAVE_46_API
+ else {
+ php_intl_idn_to_46(INTERNAL_FUNCTION_PARAM_PASSTHRU, domain, domain_len,
+ (uint32_t)option, mode, idna_info);
+ }
+#endif
+}
+
+/* {{{ proto int idn_to_ascii(string domain[, int options[, int variant[, array &idna_info]]])
+ Converts an Unicode domain to ASCII representation, as defined in the IDNA RFC */
+PHP_FUNCTION(idn_to_ascii)
+{
+ php_intl_idn_handoff(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_ASCII);
+}
+/* }}} */
+
+
+/* {{{ proto int idn_to_utf8(string domain[, int options[, int variant[, array &idna_info]]])
+ Converts an ASCII representation of the domain to Unicode (UTF-8), as defined in the IDNA RFC */
+PHP_FUNCTION(idn_to_utf8)
+{
+ php_intl_idn_handoff(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTL_IDN_TO_UTF8);
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sw=4 ts=4
+ */
diff --git a/ext/intl/idn/idn.h b/ext/intl/idn/idn.h
new file mode 100644
index 0000000..1fa4f8f
--- /dev/null
+++ b/ext/intl/idn/idn.h
@@ -0,0 +1,30 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 2009 The PHP Group |
+ +----------------------------------------------------------------------+
+ | 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: Pierre A. Joye <pierre@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ s*/
+
+#ifndef IDN_IDN_H
+#define IDN_IDN_H
+
+#include <php.h>
+
+PHP_FUNCTION(idn_to_ascii);
+PHP_FUNCTION(idn_to_utf8);
+
+void idn_register_constants(INIT_FUNC_ARGS);
+
+#endif /* IDN_IDN_H */
diff --git a/ext/intl/intl_common.h b/ext/intl/intl_common.h
new file mode 100644
index 0000000..9424571
--- /dev/null
+++ b/ext/intl/intl_common.h
@@ -0,0 +1,44 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_COMMON_H
+#define INTL_COMMON_H
+/* Auxiliary macros */
+
+#include <php.h>
+#include <unicode/utypes.h>
+
+#ifndef UBYTES
+# define UBYTES(len) ((len) * sizeof(UChar))
+#endif
+
+#ifndef eumalloc
+# define eumalloc(size) (UChar*)safe_emalloc(size, sizeof(UChar), 0)
+#endif
+
+#ifndef eurealloc
+# define eurealloc(ptr, size) (UChar*)erealloc((ptr), size * sizeof(UChar))
+#endif
+
+#define USIZE(data) sizeof((data))/sizeof(UChar)
+#define UCHARS(len) ((len) / sizeof(UChar))
+
+#define INTL_Z_STRVAL_P(str) (UChar*) Z_STRVAL_P(str)
+#define INTL_Z_STRLEN_P(str) UCHARS( Z_STRLEN_P(str) )
+
+#endif /* INTL_COMMON_H */
diff --git a/ext/intl/intl_convert.c b/ext/intl/intl_convert.c
new file mode 100644
index 0000000..92cdc4c
--- /dev/null
+++ b/ext/intl/intl_convert.c
@@ -0,0 +1,157 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.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 occurred.
+ * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough).
+ * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty).
+ */
+ 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 occurred.
+ * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough).
+ * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty).
+ */
+ 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 100644
index 0000000..3fc03f4
--- /dev/null
+++ b/ext/intl/intl_convert.h
@@ -0,0 +1,33 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef INTL_CONVERT_H
+#define INTL_CONVERT_H
+
+#include <unicode/ustring.h>
+
+void intl_convert_utf8_to_utf16(
+ UChar** target, int* target_len,
+ const char* src, int src_len,
+ UErrorCode* status );
+
+void intl_convert_utf16_to_utf8(
+ char** target, int* target_len,
+ const UChar* src, int src_len,
+ UErrorCode* status );
+
+#endif // INTL_CONVERT_H
diff --git a/ext/intl/intl_data.h b/ext/intl/intl_data.h
new file mode 100644
index 0000000..66ca27a
--- /dev/null
+++ b/ext/intl/intl_data.h
@@ -0,0 +1,107 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 ); \
+
+/* Check status by error code, if error - exit */
+#define INTL_CHECK_STATUS(err, msg) \
+ intl_error_set_code( NULL, (err) TSRMLS_CC ); \
+ if( U_FAILURE((err)) ) \
+ { \
+ intl_error_set_custom_msg( NULL, msg, 0 TSRMLS_CC ); \
+ RETURN_FALSE; \
+ }
+
+/* Check status in object, if error - exit */
+#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; \
+ }
+
+/* Check status, if error - destroy value and exit */
+#define INTL_CTOR_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 ); \
+ zval_dtor(return_value); \
+ RETURN_NULL(); \
+ }
+
+#define INTL_METHOD_RETVAL_UTF8(obj, ustring, ulen, free_it) \
+{ \
+ char *u8value; \
+ int u8len; \
+ intl_convert_utf16_to_utf8(&u8value, &u8len, ustring, ulen, &INTL_DATA_ERROR_CODE((obj))); \
+ if((free_it)) { \
+ efree(ustring); \
+ } \
+ INTL_METHOD_CHECK_STATUS((obj), "Error converting value to UTF-8"); \
+ RETVAL_STRINGL(u8value, u8len, 0); \
+}
+
+#define INTL_MAX_LOCALE_LEN 80
+
+#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 80 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 80 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 100644
index 0000000..9c2e13d
--- /dev/null
+++ b/ext/intl/intl_error.c
@@ -0,0 +1,233 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 ) {
+ 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 && INTL_G(error_level)) {
+ php_error_docref(NULL TSRMLS_CC, INTL_G(error_level), "%s", msg);
+ }
+ 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_set( intl_error* err, UErrorCode code, char* msg, int copyMsg )
+ * Set error code and message.
+ */
+void intl_errors_set( intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC )
+{
+ intl_errors_set_code( err, code TSRMLS_CC );
+ intl_errors_set_custom_msg( err, msg, copyMsg TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void intl_errors_reset( intl_error* err )
+ */
+void intl_errors_reset( intl_error* err TSRMLS_DC )
+{
+ if(err) {
+ 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 )
+{
+ if(err) {
+ 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 )
+{
+ if(err) {
+ 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 100644
index 0000000..3adae85
--- /dev/null
+++ b/ext/intl/intl_error.h
@@ -0,0 +1,47 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | 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 );
+void intl_errors_set( intl_error* err, UErrorCode code, char* msg, int copyMsg TSRMLS_DC );
+
+#endif // INTL_ERROR_H
diff --git a/ext/intl/locale/locale.c b/ext/intl/locale/locale.c
new file mode 100644
index 0000000..ffc463f
--- /dev/null
+++ b/ext/intl/locale/locale.c
@@ -0,0 +1,72 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "locale_class.h"
+#include "locale.h"
+
+#include <unicode/utypes.h>
+#include <unicode/uloc.h>
+#include <unicode/ustring.h>
+
+/* {{{ locale_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void locale_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Locale_ce_ptr )
+ {
+ zend_error( E_ERROR, "Locale class not defined" );
+ return;
+ }
+
+ #define LOCALE_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define LOCALE_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Locale_ce_ptr, ZEND_STRS( #x ) - 1, ULOC_##x TSRMLS_CC );
+ #define LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR(name, value) zend_declare_class_constant_string( Locale_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ LOCALE_EXPOSE_CLASS_CONST( ACTUAL_LOCALE );
+ LOCALE_EXPOSE_CLASS_CONST( VALID_LOCALE );
+
+ zend_declare_class_constant_null(Locale_ce_ptr, ZEND_STRS("DEFAULT_LOCALE") - 1 TSRMLS_CC);
+
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "LANG_TAG", LOC_LANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "EXTLANG_TAG", LOC_EXTLANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "SCRIPT_TAG", LOC_SCRIPT_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "REGION_TAG", LOC_REGION_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "VARIANT_TAG",LOC_VARIANT_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "GRANDFATHERED_LANG_TAG",LOC_GRANDFATHERED_LANG_TAG);
+ LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR( "PRIVATE_TAG",LOC_PRIVATE_TAG);
+
+ #undef LOCALE_EXPOSE_CUSTOM_CLASS_CONST_STR
+ #undef LOCALE_EXPOSE_CLASS_CONST
+ #undef LOCALE_EXPOSE_CONST
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/locale/locale.h b/ext/intl/locale/locale.h
new file mode 100644
index 0000000..f3859c7
--- /dev/null
+++ b/ext/intl/locale/locale.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#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 100644
index 0000000..432cfb2
--- /dev/null
+++ b/ext/intl/locale/locale_class.c
@@ -0,0 +1,121 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#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: when modifying 'locale_XX_args' do not forget to modify
+ * approptiate 'locale_XX_args' for the procedural API!
+ */
+
+ZEND_BEGIN_ARG_INFO_EX( locale_0_args, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( locale_1_arg, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, arg1 )
+ZEND_END_ARG_INFO()
+
+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()
+
+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()
+
+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
+ */
+
+zend_function_entry Locale_class_functions[] = {
+ ZEND_FENTRY( getDefault, zif_locale_get_default , locale_0_args , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( setDefault, zif_locale_set_default , locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( getPrimaryLanguage, ZEND_FN( locale_get_primary_language ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ 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 )
+ ZEND_FENTRY( acceptFromHttp, ZEND_FN( locale_accept_from_http ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 100644
index 0000000..cbddccb
--- /dev/null
+++ b/ext/intl/locale/locale_class.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#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 100644
index 0000000..1707c69
--- /dev/null
+++ b/ext/intl/locale/locale_methods.c
@@ -0,0 +1,1610 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include <unicode/udata.h>
+#include <unicode/putil.h>
+#include <unicode/ures.h>
+
+#include "php_intl.h"
+#include "locale.h"
+#include "locale_class.h"
+#include "locale_methods.h"
+#include "intl_convert.h"
+#include "intl_data.h"
+
+#include <zend_API.h>
+#include <zend.h>
+#include <php.h>
+#include "main/php_ini.h"
+#include "ext/standard/php_smart_str.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
+
+#define LOC_NOT_FOUND 1
+
+/* 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;
+
+ for(i=savedPos-1; i>=0; i--) {
+ if(isIDSeparator(*(str+i)) ){
+ /* delimiter found; check for singleton */
+ if(i>=2 && isIDSeparator(*(str+i-2)) ){
+ /* a singleton; so send the position of token before the singleton */
+ result = i-2;
+ } else {
+ result = i;
+ }
+ 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( )
+ Get default locale */
+/* }}} */
+/* {{{ proto static string locale_get_default( )
+ Get default locale */
+PHP_NAMED_FUNCTION(zif_locale_get_default)
+{
+ if( INTL_G(default_locale) == NULL ) {
+ INTL_G(default_locale) = pestrdup( uloc_getDefault(), 1);
+ }
+ RETURN_STRING( INTL_G(default_locale), TRUE );
+}
+
+/* }}} */
+
+/* {{{ proto static string Locale::setDefault( string $locale )
+ Set default locale */
+/* }}} */
+/* {{{ proto static string locale_set_default( string $locale )
+ Set default locale */
+PHP_NAMED_FUNCTION(zif_locale_set_default)
+{
+ char* locale_name = NULL;
+ int len=0;
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &locale_name ,&len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_set_default: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(len == 0) {
+ locale_name = (char *)uloc_getDefault() ;
+ len = strlen(locale_name);
+ }
+
+ zend_alter_ini_entry(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME), locale_name, len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{
+* Gets the value from ICU
+* common code shared by get_primary_language,get_script or get_region or get_variant
+* result = 0 if error, 1 if successful , -1 if no value
+*/
+static char* get_icu_value_internal( char* loc_name , char* tag_name, int* result , int fromParseLocale)
+{
+ char* tag_value = NULL;
+ int32_t tag_value_len = 512;
+
+ int singletonPos = 0;
+ char* mod_loc_name = NULL;
+ int grOffset = 0;
+
+ int32_t buflen = 512;
+ UErrorCode status = U_ZERO_ERROR;
+
+
+ if( strcmp(tag_name, LOC_CANONICALIZE_TAG) != 0 ){
+ /* Handle grandfathered languages */
+ grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
+ if( grOffset >= 0 ){
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ tag_value = estrdup(loc_name);
+ return tag_value;
+ } else {
+ /* Since Grandfathered , no value , do nothing , retutn NULL */
+ return NULL;
+ }
+ }
+
+ if( fromParseLocale==1 ){
+ /* Handle singletons */
+ if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
+ if( strlen(loc_name)>1 && (isIDPrefix(loc_name) ==1 ) ){
+ return loc_name;
+ }
+ }
+
+ singletonPos = getSingletonPos( loc_name );
+ if( singletonPos == 0){
+ /* singleton at start of script, region , variant etc.
+ * or invalid singleton at start of language */
+ return NULL;
+ } else if( singletonPos > 0 ){
+ /* singleton at some position except at start
+ * strip off the singleton and rest of the loc_name */
+ mod_loc_name = estrndup ( loc_name , singletonPos-1);
+ }
+ } /* end of if fromParse */
+
+ } /* end of if != LOC_CANONICAL_TAG */
+
+ if( mod_loc_name == NULL){
+ mod_loc_name = estrdup(loc_name );
+ }
+
+ /* Proceed to ICU */
+ do{
+ tag_value = erealloc( tag_value , buflen );
+ tag_value_len = buflen;
+
+ if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
+ buflen = uloc_getScript ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
+ buflen = uloc_getLanguage ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
+ buflen = uloc_getCountry ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
+ buflen = uloc_getVariant ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+ if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
+ buflen = uloc_canonicalize ( mod_loc_name ,tag_value , tag_value_len , &status);
+ }
+
+ if( U_FAILURE( status ) ) {
+ if( status == U_BUFFER_OVERFLOW_ERROR ) {
+ status = U_ZERO_ERROR;
+ continue;
+ }
+
+ /* Error in retriving data */
+ *result = 0;
+ if( tag_value ){
+ efree( tag_value );
+ }
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return NULL;
+ }
+ } while( buflen > tag_value_len );
+
+ if( buflen ==0 ){
+ /* No value found */
+ *result = -1;
+ if( tag_value ){
+ efree( tag_value );
+ }
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return NULL;
+ } else {
+ *result = 1;
+ }
+
+ if( mod_loc_name ){
+ efree( mod_loc_name);
+ }
+ return tag_value;
+}
+/* }}} */
+
+/* {{{
+* Gets the value from ICU , called when PHP userspace function is called
+* common code shared by get_primary_language,get_script or get_region or get_variant
+*/
+static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
+{
+
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ char* tag_value = NULL;
+ char* empty_result = "";
+
+ int result = 0;
+ char* msg = NULL;
+
+ UErrorCode status = U_ZERO_ERROR;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ 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_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ /* Call ICU get */
+ tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
+
+ /* No value found */
+ if( result == -1 ) {
+ if( tag_value){
+ efree( tag_value);
+ }
+ RETURN_STRING( empty_result , TRUE);
+ }
+
+ /* value found */
+ if( tag_value){
+ RETURN_STRING( tag_value , FALSE);
+ }
+
+ /* Error encountered while fetching the value */
+ if( result ==0) {
+ spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
+ intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_NULL();
+ }
+
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getScript($locale)
+ * gets the script for the $locale
+ }}} */
+/* {{{ proto 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 static string Locale::getRegion($locale)
+ * gets the region for the $locale
+ }}} */
+/* {{{ proto 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 static string Locale::getPrimaryLanguage($locale)
+ * gets the primary language for the $locale
+ }}} */
+/* {{{ proto 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;
+ int free_loc_name = 0;
+
+ UChar* disp_name = NULL;
+ int32_t disp_name_len = 0;
+
+ char* mod_loc_name = NULL;
+
+ int32_t buflen = 512;
+ UErrorCode status = U_ZERO_ERROR;
+
+ char* utf8value = NULL;
+ int utf8value_len = 0;
+
+ char* msg = NULL;
+ int grOffset = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
+ &loc_name, &loc_name_len ,
+ &disp_loc_name ,&disp_loc_name_len ) == FAILURE)
+ {
+ spprintf(&msg , 0, "locale_get_display_%s : unable to parse input params", tag_name );
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ if( strcmp(tag_name, DISP_NAME) != 0 ){
+ /* 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;
+ }
+ }
+ } /* end of if != LOC_CANONICAL_TAG */
+
+ if( mod_loc_name==NULL ){
+ mod_loc_name = estrdup( loc_name );
+ }
+
+ /* Check if disp_loc_name passed , if not use default locale */
+ if( !disp_loc_name){
+ disp_loc_name = estrdup(INTL_G(default_locale));
+ free_loc_name = 1;
+ }
+
+ /* Get the disp_value for the given locale */
+ do{
+ disp_name = erealloc( disp_name , buflen * sizeof(UChar) );
+ disp_name_len = buflen;
+
+ 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);
+ }
+
+ /* U_STRING_NOT_TERMINATED_WARNING is admissible here; don't look for it */
+ 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 );
+ }
+ if (free_loc_name) {
+ efree(disp_loc_name);
+ disp_loc_name = NULL;
+ }
+ RETURN_FALSE;
+ }
+ } while( buflen > disp_name_len );
+
+ if( mod_loc_name){
+ efree( mod_loc_name );
+ }
+ if (free_loc_name) {
+ efree(disp_loc_name);
+ disp_loc_name = NULL;
+ }
+ /* Convert display locale name from UTF-16 to UTF-8. */
+ intl_convert_utf16_to_utf8( &utf8value, &utf8value_len, disp_name, buflen, &status );
+ efree( disp_name );
+ if( U_FAILURE( status ) )
+ {
+ spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
+ intl_error_set( NULL, status, msg , 1 TSRMLS_CC );
+ efree(msg);
+ RETURN_FALSE;
+ }
+
+ RETVAL_STRINGL( utf8value, utf8value_len , FALSE);
+
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getDisplayName($locale[, $in_locale = null])
+* gets the name for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{ proto 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 );
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getDisplayLanguage($locale[, $in_locale = null])
+* gets the language for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{ proto 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 );
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getDisplayScript($locale, $in_locale = null)
+* gets the script for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{ proto 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 );
+}
+/* }}} */
+
+/* {{{ proto static string Locale::getDisplayRegion($locale, $in_locale = null)
+* gets the region for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{ proto 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 );
+}
+/* }}} */
+
+/* {{{
+* proto static string Locale::getDisplayVariant($locale, $in_locale = null)
+* gets the variant for the $locale in $in_locale or default_locale
+ }}} */
+/* {{{
+* proto 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 array getKeywords(string $locale) {
+ * return an associative array containing keyword-value
+ * pairs for this locale. The keys are keys to the array (doh!)
+ * }}}*/
+ /* {{{ proto static array 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 );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_get_keywords: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ /* Get the keywords */
+ e = uloc_openKeywords( loc_name, &status );
+ if( e != NULL )
+ {
+ /* Traverse it, filling the return array. */
+ array_init( return_value );
+
+ while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
+ kw_value = ecalloc( 1 , kw_value_len );
+
+ /* Get the keyword value for each keyword */
+ kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len , &status );
+ if (status == U_BUFFER_OVERFLOW_ERROR) {
+ status = U_ZERO_ERROR;
+ kw_value = erealloc( kw_value , kw_value_len+1);
+ kw_value_len=uloc_getKeywordValue( loc_name,kw_key, kw_value, kw_value_len+1 , &status );
+ } else if(!U_FAILURE(status)) {
+ kw_value = erealloc( kw_value , kw_value_len+1);
+ }
+ if (U_FAILURE(status)) {
+ intl_error_set( NULL, FAILURE, "locale_get_keywords: Error encountered while getting the keyword value for the keyword", 0 TSRMLS_CC );
+ if( kw_value){
+ efree( kw_value );
+ }
+ zval_dtor(return_value);
+ RETURN_FALSE;
+ }
+
+ add_assoc_stringl( return_value, (char *)kw_key, kw_value , kw_value_len, 0);
+ } /* end of while */
+
+ } /* end of if e!=NULL */
+
+ uenum_close( e );
+}
+/* }}} */
+
+ /* {{{ proto static string Locale::canonicalize($locale)
+ * @return string the canonicalized locale
+ * }}} */
+ /* {{{ proto static string locale_canonicalize(Locale $loc, string $locale)
+ * @param string $locale The locale string to canonicalize
+ */
+PHP_FUNCTION(locale_canonicalize)
+{
+ get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
+}
+/* }}} */
+
+/* {{{ append_key_value
+* Internal function which is called from locale_compose
+* gets the value for the key_name and appends to the loc_name
+* returns 1 if successful , -1 if not found ,
+* 0 if array element is not a string , -2 if buffer-overflow
+*/
+static int append_key_value(smart_str* loc_name, HashTable* hash_arr, char* key_name)
+{
+ zval** ele_value = NULL;
+
+ if(zend_hash_find(hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ) {
+ if(Z_TYPE_PP(ele_value)!= IS_STRING ){
+ /* element value is not a string */
+ return FAILURE;
+ }
+ if(strcmp(key_name, LOC_LANG_TAG) != 0 &&
+ strcmp(key_name, LOC_GRANDFATHERED_LANG_TAG)!=0 ) {
+ /* not lang or grandfathered tag */
+ smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
+ }
+ smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
+ return SUCCESS;
+ }
+
+ return LOC_NOT_FOUND;
+}
+/* }}} */
+
+/* {{{ append_prefix , appends the prefix needed
+* e.g. private adds 'x'
+*/
+static void add_prefix(smart_str* loc_name, char* key_name)
+{
+ if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
+ smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
+ smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
+ }
+}
+/* }}} */
+
+/* {{{ 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(smart_str* loc_name, HashTable* hash_arr, char* key_name TSRMLS_DC)
+{
+ zval** ele_value = NULL;
+ int i = 0;
+ int isFirstSubtag = 0;
+ int max_value = 0;
+
+ /* Variant/ Extlang/Private etc. */
+ if( zend_hash_find( hash_arr , key_name , strlen(key_name) + 1 ,(void **)&ele_value ) == SUCCESS ) {
+ if( Z_TYPE_PP(ele_value) == IS_STRING ){
+ add_prefix( loc_name , key_name);
+
+ smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
+ smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
+ return SUCCESS;
+ } else if(Z_TYPE_PP(ele_value) == IS_ARRAY ) {
+ HashPosition pos;
+ HashTable *arr = HASH_OF(*ele_value);
+ zval **data = NULL;
+
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ while(zend_hash_get_current_data_ex(arr, (void **)&data, &pos) != FAILURE) {
+ if(Z_TYPE_PP(data) != IS_STRING) {
+ return FAILURE;
+ }
+ if (isFirstSubtag++ == 0){
+ add_prefix(loc_name , key_name);
+ }
+ smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
+ smart_str_appendl(loc_name, Z_STRVAL_PP(data) , Z_STRLEN_PP(data));
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+ } else {
+ char cur_key_name[31];
+ /* 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 */
+ isFirstSubtag = 0;
+ for( i=0 ; i< max_value; i++ ){
+ snprintf( cur_key_name , 30, "%s%d", key_name , i);
+ if( zend_hash_find( hash_arr , cur_key_name , strlen(cur_key_name) + 1,(void **)&ele_value ) == SUCCESS ){
+ if( Z_TYPE_PP(ele_value)!= IS_STRING ){
+ /* variant is not a string */
+ return FAILURE;
+ }
+ /* Add the contents */
+ if (isFirstSubtag++ == 0){
+ add_prefix(loc_name , cur_key_name);
+ }
+ smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
+ smart_str_appendl(loc_name, Z_STRVAL_PP(ele_value) , Z_STRLEN_PP(ele_value));
+ }
+ } /* end of for */
+ } /* end of else */
+
+ return SUCCESS;
+}
+/* }}} */
+
+/*{{{
+* 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, smart_str* loc_name TSRMLS_DC)
+{
+ intl_error_reset( NULL TSRMLS_CC );
+ if( result == FAILURE) {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_compose: parameter array element is not a string", 0 TSRMLS_CC );
+ smart_str_free(loc_name);
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+#define RETURN_SMART_STR(s) smart_str_0((s)); RETURN_STRINGL((s)->c, (s)->len, 0)
+/* {{{ 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)
+{
+ smart_str loc_name_s = {0};
+ smart_str *loc_name = &loc_name_s;
+ zval* arr = NULL;
+ HashTable* hash_arr = NULL;
+ int result = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ 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;
+
+ /* Check for grandfathered first */
+ result = append_key_value(loc_name, hash_arr, LOC_GRANDFATHERED_LANG_TAG);
+ if( result == SUCCESS){
+ RETURN_SMART_STR(loc_name);
+ }
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Not grandfathered */
+ result = append_key_value(loc_name, hash_arr , LOC_LANG_TAG);
+ if( result == LOC_NOT_FOUND ){
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_compose: parameter array does not contain 'language' tag.", 0 TSRMLS_CC );
+ smart_str_free(loc_name);
+ RETURN_FALSE;
+ }
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Extlang */
+ result = append_multiple_key_values(loc_name, hash_arr , LOC_EXTLANG_TAG TSRMLS_CC);
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Script */
+ result = append_key_value(loc_name, hash_arr , LOC_SCRIPT_TAG);
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Region */
+ result = append_key_value( loc_name, hash_arr , LOC_REGION_TAG);
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Variant */
+ result = append_multiple_key_values( loc_name, hash_arr , LOC_VARIANT_TAG TSRMLS_CC);
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ /* Private */
+ result = append_multiple_key_values( loc_name, hash_arr , LOC_PRIVATE_TAG TSRMLS_CC);
+ if( !handleAppendResult( result, loc_name TSRMLS_CC)){
+ RETURN_FALSE;
+ }
+
+ RETURN_SMART_STR(loc_name);
+}
+/* }}} */
+
+
+/*{{{
+* Parses the locale and returns private subtags if existing
+* else returns NULL
+* e.g. for locale='en_US-x-prv1-prv2-prv3'
+* returns a pointer to the string 'prv1-prv2-prv3'
+*/
+static char* get_private_subtags(char* loc_name)
+{
+ char* result =NULL;
+ int singletonPos = 0;
+ int len =0;
+ char* mod_loc_name =NULL;
+
+ if( loc_name && (len = strlen(loc_name)>0 ) ){
+ mod_loc_name = loc_name ;
+ len = strlen(mod_loc_name);
+ while( (singletonPos = getSingletonPos(mod_loc_name))!= -1){
+
+ if( singletonPos!=-1){
+ if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
+ /* private subtag start found */
+ if( singletonPos + 2 == len){
+ /* loc_name ends with '-x-' ; return NULL */
+ }
+ else{
+ /* result = mod_loc_name + singletonPos +2; */
+ result = estrndup(mod_loc_name + singletonPos+2 , (len -( singletonPos +2) ) );
+ }
+ break;
+ }
+ else{
+ if( singletonPos + 1 >= len){
+ /* String end */
+ break;
+ } else {
+ /* singleton found but not a private subtag , hence check further in the string for the private subtag */
+ mod_loc_name = mod_loc_name + singletonPos +1;
+ len = strlen(mod_loc_name);
+ }
+ }
+ }
+
+ } /* end of while */
+ }
+
+ return result;
+}
+/* }}} */
+
+/* {{{ code used by locale_parse
+*/
+static int add_array_entry(char* loc_name, zval* hash_arr, char* key_name TSRMLS_DC)
+{
+ char* key_value = NULL;
+ char* cur_key_name = NULL;
+ char* token = NULL;
+ char* last_ptr = NULL;
+
+ int result = 0;
+ int cur_result = 0;
+ int cnt = 0;
+
+
+ if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
+ key_value = get_private_subtags( loc_name );
+ result = 1;
+ } else {
+ key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
+ }
+ if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) ||
+ ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
+ if( result > 0 && key_value){
+ /* Tokenize on the "_" or "-" */
+ token = php_strtok_r( key_value , DELIMITER ,&last_ptr);
+ if( cur_key_name ){
+ efree( cur_key_name);
+ }
+ cur_key_name = (char*)ecalloc( 25, 25);
+ sprintf( cur_key_name , "%s%d", key_name , cnt++);
+ add_assoc_string( hash_arr, cur_key_name , token ,TRUE );
+ /* tokenize on the "_" or "-" and stop at singleton if any */
+ while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
+ sprintf( cur_key_name , "%s%d", key_name , cnt++);
+ add_assoc_string( hash_arr, cur_key_name , token , TRUE );
+ }
+/*
+ if( strcmp(key_name, LOC_PRIVATE_TAG) == 0 ){
+ }
+*/
+ }
+ } else {
+ if( result == 1 ){
+ add_assoc_string( hash_arr, key_name , key_value , TRUE );
+ cur_result = 1;
+ }
+ }
+
+ if( cur_key_name ){
+ efree( cur_key_name);
+ }
+ /*if( key_name != LOC_PRIVATE_TAG && key_value){*/
+ if( key_value){
+ efree(key_value);
+ }
+ return cur_result;
+}
+/* }}} */
+
+/* {{{ proto static array Locale::parseLocale($locale)
+* parses a locale-id into an array the different parts of it
+ }}} */
+/* {{{ proto static array parse_locale($locale)
+* parses a locale-id into an array the different parts of it
+*/
+PHP_FUNCTION(locale_parse)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+ int grOffset = 0;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+ array_init( return_value );
+
+ grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
+ if( grOffset >= 0 ){
+ add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG , estrdup(loc_name) ,FALSE );
+ }
+ else{
+ /* Not grandfathered */
+ add_array_entry( loc_name , return_value , LOC_LANG_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_REGION_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_VARIANT_TAG TSRMLS_CC);
+ add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ proto static array Locale::getAllVariants($locale)
+* gets an array containing the list of variants, or null
+ }}} */
+/* {{{ proto static array locale_get_all_variants($locale)
+* gets an array containing the list of variants, or null
+*/
+PHP_FUNCTION(locale_get_all_variants)
+{
+ char* loc_name = NULL;
+ int loc_name_len = 0;
+
+ int result = 0;
+ char* token = NULL;
+ char* variant = NULL;
+ char* saved_ptr = NULL;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &loc_name, &loc_name_len ) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_name_len == 0) {
+ loc_name = INTL_G(default_locale);
+ }
+
+
+ array_init( return_value );
+
+ /* If the locale is grandfathered, stop, no variants */
+ if( findOffset( LOC_GRANDFATHERED , loc_name ) >= 0 ){
+ /* ("Grandfathered Tag. No variants."); */
+ }
+ else {
+ /* Call ICU variant */
+ variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
+ if( result > 0 && variant){
+ /* Tokenize on the "_" or "-" */
+ token = php_strtok_r( variant , DELIMITER , &saved_ptr);
+ add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
+ /* tokenize on the "_" or "-" and stop at singleton if any */
+ while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
+ add_next_index_stringl( return_value, token , strlen(token) ,TRUE );
+ }
+ }
+ if( variant ){
+ efree( variant );
+ }
+ }
+
+
+}
+/* }}} */
+
+/*{{{
+* Converts to lower case and also replaces all 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) || str[0] == '\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);
+}
+/* }}} */
+
+/* {{{ proto static boolean Locale::filterMatches(string $langtag, string $locale[, bool $canonicalize])
+* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
+*/
+/* }}} */
+/* {{{ proto boolean locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])
+* Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
+*/
+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 );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss|b",
+ &lang_tag, &lang_tag_len , &loc_range , &loc_range_len ,
+ &boolCanonical) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_filter_matches: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ if(loc_range_len == 0) {
+ loc_range = INTL_G(default_locale);
+ }
+
+ if( strcmp(loc_range,"*")==0){
+ RETURN_TRUE;
+ }
+
+ if( boolCanonical ){
+ /* canonicalize loc_range */
+ can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
+ if( result ==0) {
+ intl_error_set( NULL, status,
+ "locale_filter_matches : unable to canonicalize loc_range" , 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* canonicalize lang_tag */
+ can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result , 0);
+ if( result ==0) {
+ intl_error_set( NULL, status,
+ "locale_filter_matches : unable to canonicalize lang_tag" , 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* Convert to lower case for case-insensitive comparison */
+ cur_lang_tag = ecalloc( 1, strlen(can_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(can_loc_range) + 1);
+ result = strToMatch( can_loc_range , cur_loc_range );
+ if( result == 0) {
+ efree( cur_lang_tag );
+ efree( can_lang_tag );
+ efree( cur_loc_range );
+ efree( can_loc_range );
+ RETURN_FALSE;
+ }
+
+ /* check if prefix */
+ token = strstr( cur_lang_tag , cur_loc_range );
+
+ if( token && (token==cur_lang_tag) ){
+ /* check if the char. after match is SEPARATOR */
+ chrcheck = token + (strlen(cur_loc_range));
+ if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ if( can_lang_tag){
+ efree( can_lang_tag );
+ }
+ if( can_loc_range){
+ efree( can_loc_range );
+ }
+ RETURN_TRUE;
+ }
+ }
+
+ /* No prefix as loc_range */
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ if( can_lang_tag){
+ efree( can_lang_tag );
+ }
+ if( can_loc_range){
+ efree( can_loc_range );
+ }
+ RETURN_FALSE;
+
+ } /* end of if isCanonical */
+ else{
+ /* Convert to lower case for case-insensitive comparison */
+ cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
+
+ result = strToMatch( lang_tag , cur_lang_tag);
+ if( result == 0) {
+ efree( cur_lang_tag );
+ RETURN_FALSE;
+ }
+ cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
+ result = strToMatch( loc_range , cur_loc_range );
+ if( result == 0) {
+ efree( cur_lang_tag );
+ efree( cur_loc_range );
+ RETURN_FALSE;
+ }
+
+ /* check if prefix */
+ token = strstr( cur_lang_tag , cur_loc_range );
+
+ if( token && (token==cur_lang_tag) ){
+ /* check if the char. after match is SEPARATOR */
+ chrcheck = token + (strlen(cur_loc_range));
+ if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ RETURN_TRUE;
+ }
+ }
+
+ /* No prefix as loc_range */
+ if( cur_lang_tag){
+ efree( cur_lang_tag );
+ }
+ if( cur_loc_range){
+ efree( cur_loc_range );
+ }
+ RETURN_FALSE;
+
+ }
+}
+/* }}} */
+
+static void array_cleanup( char* arr[] , int arr_size)
+{
+ int i=0;
+ for( i=0; i< arr_size; i++ ){
+ if( arr[i*2] ){
+ efree( arr[i*2]);
+ }
+ }
+ efree(arr);
+}
+
+#define LOOKUP_CLEAN_RETURN(value) array_cleanup(cur_arr, cur_arr_len); return (value)
+/* {{{
+* returns the lookup result to lookup_loc_range_src_php
+* internal function
+*/
+static char* lookup_loc_range(char* loc_range, HashTable* hash_arr, int canonicalize TSRMLS_DC)
+{
+ int i = 0;
+ int cur_arr_len = 0;
+ int result = 0;
+
+ char* lang_tag = NULL;
+ zval** ele_value = NULL;
+ char** cur_arr = NULL;
+
+ char* cur_loc_range = NULL;
+ char* can_loc_range = NULL;
+ int saved_pos = 0;
+
+ char* return_value = NULL;
+
+ cur_arr = ecalloc(zend_hash_num_elements(hash_arr)*2, sizeof(char *));
+ /* 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(Z_TYPE_PP(ele_value)!= IS_STRING) {
+ /* element value is not a string */
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: locale array element is not a string", 0 TSRMLS_CC);
+ LOOKUP_CLEAN_RETURN(NULL);
+ }
+ cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_PP(ele_value), Z_STRLEN_PP(ele_value));
+ result = strToMatch(Z_STRVAL_PP(ele_value), cur_arr[cur_arr_len*2]);
+ if(result == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0 TSRMLS_CC);
+ LOOKUP_CLEAN_RETURN(NULL);
+ }
+ cur_arr[cur_arr_len*2+1] = Z_STRVAL_PP(ele_value);
+ cur_arr_len++ ;
+ } /* end of for */
+
+ /* Canonicalize array elements */
+ if(canonicalize) {
+ for(i=0; i<cur_arr_len; i++) {
+ lang_tag = get_icu_value_internal(cur_arr[i*2], LOC_CANONICALIZE_TAG, &result, 0);
+ if(result != 1 || lang_tag == NULL || !lang_tag[0]) {
+ if(lang_tag) {
+ efree(lang_tag);
+ }
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
+ LOOKUP_CLEAN_RETURN(NULL);
+ }
+ cur_arr[i*2] = erealloc(cur_arr[i*2], strlen(lang_tag)+1);
+ result = strToMatch(lang_tag, cur_arr[i*2]);
+ efree(lang_tag);
+ if(result == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
+ LOOKUP_CLEAN_RETURN(NULL);
+ }
+ }
+
+ }
+
+ if(canonicalize) {
+ /* Canonicalize the loc_range */
+ can_loc_range = get_icu_value_internal(loc_range, LOC_CANONICALIZE_TAG, &result , 0);
+ if( result != 1 || can_loc_range == NULL || !can_loc_range[0]) {
+ /* Error */
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize loc_range" , 0 TSRMLS_CC );
+ if(can_loc_range) {
+ efree(can_loc_range);
+ }
+ LOOKUP_CLEAN_RETURN(NULL);
+ } else {
+ loc_range = can_loc_range;
+ }
+ }
+
+ cur_loc_range = ecalloc(1, strlen(loc_range)+1);
+ /* convert to lower and replace hyphens */
+ result = strToMatch(loc_range, cur_loc_range);
+ if(can_loc_range) {
+ efree(can_loc_range);
+ }
+ if(result == 0) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0 TSRMLS_CC);
+ LOOKUP_CLEAN_RETURN(NULL);
+ }
+
+ /* Lookup for the lang_tag match */
+ saved_pos = strlen(cur_loc_range);
+ while(saved_pos > 0) {
+ for(i=0; i< cur_arr_len; i++){
+ if(cur_arr[i*2] != NULL && strlen(cur_arr[i*2]) == saved_pos && strncmp(cur_loc_range, cur_arr[i*2], saved_pos) == 0) {
+ /* Match found */
+ return_value = estrdup(canonicalize?cur_arr[i*2]:cur_arr[i*2+1]);
+ efree(cur_loc_range);
+ LOOKUP_CLEAN_RETURN(return_value);
+ }
+ }
+ saved_pos = getStrrtokenPos(cur_loc_range, saved_pos);
+ }
+
+ /* Match not found */
+ efree(cur_loc_range);
+ LOOKUP_CLEAN_RETURN(NULL);
+}
+/* }}} */
+
+/* {{{ proto string Locale::lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
+* Searchs the items in $langtag for the best match to the language
+* range
+*/
+/* }}} */
+/* {{{ proto string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $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 );
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "as|bs", &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_FALSE;
+ }
+
+ if(loc_range_len == 0) {
+ loc_range = INTL_G(default_locale);
+ }
+
+ hash_arr = HASH_OF(arr);
+
+ if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) {
+ RETURN_EMPTY_STRING();
+ }
+
+ result = lookup_loc_range(loc_range, hash_arr, boolCanonical TSRMLS_CC);
+ if(result == NULL || result[0] == '\0') {
+ if( fallback_loc ) {
+ result = estrndup(fallback_loc, fallback_loc_len);
+ } else {
+ RETURN_EMPTY_STRING();
+ }
+ }
+
+ RETVAL_STRINGL(result, strlen(result), 0);
+}
+/* }}} */
+
+/* {{{ proto string Locale::acceptFromHttp(string $http_accept)
+* Tries to find out best available locale based on HTTP �Accept-Language� header
+*/
+/* }}} */
+/* {{{ proto string locale_accept_from_http(string $http_accept)
+* Tries to find out best available locale based on HTTP �Accept-Language� header
+*/
+PHP_FUNCTION(locale_accept_from_http)
+{
+ UEnumeration *available;
+ char *http_accept = NULL;
+ int http_accept_len;
+ UErrorCode status = 0;
+ int len;
+ char resultLocale[INTL_MAX_LOCALE_LEN+1];
+ UAcceptResult outResult;
+
+ if(zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &http_accept, &http_accept_len) == FAILURE)
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "locale_accept_from_http: unable to parse input parameters", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ available = ures_openAvailableLocales(NULL, &status);
+ INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to retrieve locale list");
+ len = uloc_acceptLanguageFromHTTP(resultLocale, INTL_MAX_LOCALE_LEN,
+ &outResult, http_accept, available, &status);
+ uenum_close(available);
+ INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to find acceptable locale");
+ if (len < 0 || outResult == ULOC_ACCEPT_FAILED) {
+ RETURN_FALSE;
+ }
+ RETURN_STRINGL(resultLocale, len, 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
+ *can_loc_len
+*/
diff --git a/ext/intl/locale/locale_methods.h b/ext/intl/locale/locale_methods.h
new file mode 100644
index 0000000..e88fac6
--- /dev/null
+++ b/ext/intl/locale/locale_methods.h
@@ -0,0 +1,49 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#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);
+PHP_FUNCTION( locale_accept_from_http);
+
+#endif // LOCALE_METHODS_H
diff --git a/ext/intl/msgformat/msgformat.c b/ext/intl/msgformat/msgformat.c
new file mode 100644
index 0000000..e3fb942
--- /dev/null
+++ b/ext/intl/msgformat/msgformat.c
@@ -0,0 +1,176 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+#include <unicode/umsg.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "intl_convert.h"
+
+/* {{{ */
+static void msgfmt_ctor(INTERNAL_FUNCTION_PARAMETERS)
+{
+ char* locale;
+ char* pattern;
+ int locale_len = 0, pattern_len = 0;
+ UChar* spattern = NULL;
+ int spattern_len = 0;
+ zval* object;
+ MessageFormatter_object* mfo;
+ intl_error_reset( NULL TSRMLS_CC );
+
+ object = return_value;
+ /* Parse parameters. */
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "ss",
+ &locale, &locale_len, &pattern, &pattern_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_create: unable to parse input parameters", 0 TSRMLS_CC );
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value);
+ MSG_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ /* Convert pattern (if specified) to UTF-16. */
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ INTL_CTOR_CHECK_STATUS(mfo, "msgfmt_create: error converting pattern to UTF-16");
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(locale_len == 0) {
+ locale = INTL_G(default_locale);
+ }
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+ if(msgformat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ INTL_CTOR_CHECK_STATUS(mfo, "msgfmt_create: error converting pattern to quote-friendly format");
+ }
+#endif
+
+ if ((mfo)->mf_data.orig_format) {
+ msgformat_data_free(&mfo->mf_data TSRMLS_CC);
+ }
+
+ (mfo)->mf_data.orig_format = estrndup(pattern, pattern_len);
+ (mfo)->mf_data.orig_format_len = pattern_len;
+
+ /* Create an ICU message formatter. */
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, locale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+
+ if(spattern) {
+ efree(spattern);
+ }
+
+ INTL_CTOR_CHECK_STATUS(mfo, "msgfmt_create: message formatter creation failed");
+}
+/* }}} */
+
+/* {{{ proto MessageFormatter MesssageFormatter::create( string $locale, string $pattern )
+ * Create formatter. }}} */
+/* {{{ proto MessageFormatter msgfmt_create( string $locale, string $pattern )
+ * Create formatter.
+ */
+PHP_FUNCTION( msgfmt_create )
+{
+ object_init_ex( return_value, MessageFormatter_ce_ptr );
+ msgfmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto void MessageFormatter::__construct( string $locale, string $pattern )
+ * MessageFormatter object constructor.
+ */
+PHP_METHOD( MessageFormatter, __construct )
+{
+ return_value = getThis();
+ msgfmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ 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 100644
index 0000000..205c706
--- /dev/null
+++ b/ext/intl/msgformat/msgformat.h
@@ -0,0 +1,27 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMATTER_H
+#define MSG_FORMATTER_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_create );
+PHP_FUNCTION( msgfmt_get_error_code );
+PHP_FUNCTION( msgfmt_get_error_message );
+PHP_METHOD( MessageFormatter, __construct );
+
+#endif // MSG_FORMAT_H
diff --git a/ext/intl/msgformat/msgformat_attr.c b/ext/intl/msgformat/msgformat_attr.c
new file mode 100644
index 0000000..ed2dae2
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_attr.c
@@ -0,0 +1,145 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_attr.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+
+
+/* {{{ proto string MessageFormatter::getPattern( )
+ * Get formatter pattern. }}} */
+/* {{{ proto string msgfmt_get_pattern( MessageFormatter $mf )
+ * Get formatter pattern.
+ */
+PHP_FUNCTION( msgfmt_get_pattern )
+{
+ 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_pattern: unable to parse input params", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ if(mfo->mf_data.orig_format) {
+ RETURN_STRINGL(mfo->mf_data.orig_format, mfo->mf_data.orig_format_len, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool MessageFormatter::setPattern( string $pattern )
+ * Set formatter pattern. }}} */
+/* {{{ proto bool msgfmt_set_pattern( MessageFormatter $mf, string $pattern )
+ * Set formatter pattern.
+ */
+PHP_FUNCTION( msgfmt_set_pattern )
+{
+ char* value = NULL;
+ int value_len = 0;
+ int spattern_len = 0;
+ UChar* spattern = NULL;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, MessageFormatter_ce_ptr, &value, &value_len ) == FAILURE )
+ {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_set_pattern: unable to parse input params", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ }
+
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ /* Convert given pattern to UTF-16. */
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, value, value_len, &INTL_DATA_ERROR_CODE(mfo));
+ INTL_METHOD_CHECK_STATUS(mfo, "Error converting pattern to UTF-16" );
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+ if(msgformat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_set_pattern: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+#endif
+
+ /* TODO: add parse error information */
+ umsg_applyPattern(MSG_FORMAT_OBJECT(mfo), spattern, spattern_len, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ if (spattern) {
+ efree(spattern);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Error setting symbol value");
+
+ if(mfo->mf_data.orig_format) {
+ efree(mfo->mf_data.orig_format);
+ }
+ mfo->mf_data.orig_format = estrndup(value, value_len);
+ mfo->mf_data.orig_format_len = value_len;
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string MessageFormatter::getLocale()
+ * Get formatter locale. }}} */
+/* {{{ proto string msgfmt_get_locale(MessageFormatter $mf)
+ * Get formatter locale.
+ */
+PHP_FUNCTION( msgfmt_get_locale )
+{
+ char *loc;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, MessageFormatter_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_get_locale: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ loc = (char *)umsg_getLocale(MSG_FORMAT_OBJECT(mfo));
+ RETURN_STRING(loc, 1);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/msgformat/msgformat_attr.h b/ext/intl/msgformat/msgformat_attr.h
new file mode 100644
index 0000000..898c445
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_attr.h
@@ -0,0 +1,26 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_ATTR_H
+#define MSG_FORMAT_ATTR_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_set_pattern );
+PHP_FUNCTION( msgfmt_get_pattern );
+PHP_FUNCTION( msgfmt_get_locale );
+
+#endif // MSG_FORMAT_ATTR_H
diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c
new file mode 100644
index 0000000..bb3b55f
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_class.c
@@ -0,0 +1,192 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+#include <zend_exceptions.h>
+
+zend_class_entry *MessageFormatter_ce_ptr = NULL;
+static zend_object_handlers MessageFormatter_handlers;
+
+/*
+ * 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 );
+ object_properties_init(&intern->zo, ce);
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ MessageFormatter_object_dtor,
+ (zend_objects_free_object_storage_t)MessageFormatter_object_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = &MessageFormatter_handlers;
+
+ return retval;
+}
+/* }}} */
+
+/* {{{ MessageFormatter_object_clone */
+zend_object_value MessageFormatter_object_clone(zval *object TSRMLS_DC)
+{
+ zend_object_value new_obj_val;
+ zend_object_handle handle = Z_OBJ_HANDLE_P(object);
+ MessageFormatter_object *mfo, *new_mfo;
+
+ MSG_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
+ new_obj_val = MessageFormatter_ce_ptr->create_object(Z_OBJCE_P(object) TSRMLS_CC);
+ new_mfo = (MessageFormatter_object *)zend_object_store_get_object_by_handle(new_obj_val.handle TSRMLS_CC);
+ /* clone standard parts */
+ zend_objects_clone_members(&new_mfo->zo, new_obj_val, &mfo->zo, handle TSRMLS_CC);
+
+ /* clone formatter object */
+ if (MSG_FORMAT_OBJECT(mfo) != NULL) {
+ MSG_FORMAT_OBJECT(new_mfo) = umsg_clone(MSG_FORMAT_OBJECT(mfo),
+ &INTL_DATA_ERROR_CODE(mfo));
+
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(mfo))) {
+ intl_errors_set(INTL_DATA_ERROR_P(mfo), INTL_DATA_ERROR_CODE(mfo),
+ "Failed to clone MessageFormatter object", 0 TSRMLS_CC);
+ zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Failed to clone MessageFormatter object");
+ }
+ } else {
+ zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Cannot clone unconstructed MessageFormatter");
+ }
+ return new_obj_val;
+}
+/* }}} */
+
+/*
+ * 'MessageFormatter' class registration structures & functions
+ */
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_messageformatter___construct, 0, 0, 2)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_messageformatter_geterrormessage, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_messageformatter_formatmessage, 0, 0, 3)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, pattern)
+ ZEND_ARG_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_messageformatter_format, 0, 0, 1)
+ ZEND_ARG_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_messageformatter_setpattern, 0, 0, 1)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_messageformatter_parse, 0, 0, 1)
+ ZEND_ARG_INFO(0, source)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ MessageFormatter_class_functions
+ * Every 'MessageFormatter' class method has an entry in this table
+ */
+static zend_function_entry MessageFormatter_class_functions[] = {
+ PHP_ME( MessageFormatter, __construct, arginfo_messageformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_FENTRY( create, ZEND_FN( msgfmt_create ), arginfo_messageformatter___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( format, ZEND_FN( msgfmt_format ), arginfo_messageformatter_format )
+ ZEND_FENTRY( formatMessage, ZEND_FN( msgfmt_format_message ), arginfo_messageformatter_formatmessage, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( parse, ZEND_FN( msgfmt_parse ), arginfo_messageformatter_parse )
+ ZEND_FENTRY( parseMessage, ZEND_FN( msgfmt_parse_message ), arginfo_messageformatter_formatmessage, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_NAMED_FE( setPattern, ZEND_FN( msgfmt_set_pattern ), arginfo_messageformatter_setpattern )
+ PHP_NAMED_FE( getPattern, ZEND_FN( msgfmt_get_pattern ), arginfo_messageformatter_geterrormessage )
+ PHP_NAMED_FE( getLocale, ZEND_FN( msgfmt_get_locale ), arginfo_messageformatter_geterrormessage )
+ PHP_NAMED_FE( getErrorCode, ZEND_FN( msgfmt_get_error_code ), arginfo_messageformatter_geterrormessage )
+ PHP_NAMED_FE( getErrorMessage, ZEND_FN( msgfmt_get_error_message ), arginfo_messageformatter_geterrormessage )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 );
+
+ memcpy(&MessageFormatter_handlers, zend_get_std_object_handlers(),
+ sizeof MessageFormatter_handlers);
+ MessageFormatter_handlers.clone_obj = MessageFormatter_object_clone;
+
+ /* 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 100644
index 0000000..337e04e
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_class.h
@@ -0,0 +1,55 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 <unicode/uconfig.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_NO_CHECK INTL_METHOD_FETCH_OBJECT(MessageFormatter, mfo)
+#define MSG_FORMAT_METHOD_FETCH_OBJECT \
+ MSG_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK; \
+ if (MSG_FORMAT_OBJECT(mfo) == NULL) { \
+ intl_errors_set(&mfo->mf_data.error, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Found unconstructed MessageFormatter", 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ }
+
+#define MSG_FORMAT_OBJECT(mfo) (mfo)->mf_data.umsgf
+
+#if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM < 48
+# define MSG_FORMAT_QUOTE_APOS 1
+#endif
+
+#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 100644
index 0000000..527c1d4
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_data.c
@@ -0,0 +1,102 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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"
+
+#include "msgformat_class.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;
+}
+/* }}} */
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+int msgformat_fix_quotes(UChar **spattern, uint32_t *spattern_len, UErrorCode *ec)
+{
+ if(*spattern && *spattern_len && u_strchr(*spattern, (UChar)'\'')) {
+ UChar *npattern = emalloc(sizeof(UChar)*(2*(*spattern_len)+1));
+ uint32_t npattern_len;
+ npattern_len = umsg_autoQuoteApostrophe(*spattern, *spattern_len, npattern, 2*(*spattern_len)+1, ec);
+ efree(*spattern);
+ if( U_FAILURE(*ec) )
+ {
+ return FAILURE;
+ }
+ npattern = erealloc(npattern, sizeof(UChar)*(npattern_len+1));
+ *spattern = npattern;
+ *spattern_len = npattern_len;
+ }
+ return SUCCESS;
+}
+#endif
+
+
+/*
+ * 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 100644
index 0000000..6479888
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_data.h
@@ -0,0 +1,44 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_DATA_H
+#define MSG_FORMAT_DATA_H
+
+#include <php.h>
+
+#include "../intl_error.h"
+
+#include <unicode/umsg.h>
+
+typedef struct {
+ // error hangling
+ intl_error error;
+
+ // formatter handling
+ UMessageFormat* umsgf;
+ char* orig_format;
+ ulong orig_format_len;
+} msgformat_data;
+
+msgformat_data* msgformat_data_create( TSRMLS_D );
+void msgformat_data_init( msgformat_data* mf_data TSRMLS_DC );
+void msgformat_data_free( msgformat_data* mf_data TSRMLS_DC );
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+int msgformat_fix_quotes(UChar **spattern, uint32_t *spattern_len, UErrorCode *ec);
+#endif
+
+#endif // MSG_FORMAT_DATA_H
diff --git a/ext/intl/msgformat/msgformat_format.c b/ext/intl/msgformat/msgformat_format.c
new file mode 100644
index 0000000..9a18ac0
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_format.c
@@ -0,0 +1,186 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_format.h"
+#include "msgformat_data.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+
+#ifndef Z_ADDREF_P
+#define Z_ADDREF_P(z) ((z)->refcount++)
+#endif
+
+/* {{{ */
+static void msgfmt_do_format(MessageFormatter_object *mfo, zval *args, zval *return_value TSRMLS_DC)
+{
+ zval **fargs;
+ int count;
+ UChar* formatted = NULL;
+ int formatted_len = 0;
+ HashPosition pos;
+ int i;
+
+ count = zend_hash_num_elements(Z_ARRVAL_P(args));
+
+ if(count < umsg_format_arg_count(MSG_FORMAT_OBJECT(mfo))) {
+ /* Not enough aguments for format! */
+ intl_error_set( INTL_DATA_ERROR_P(mfo), U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format: not enough parameters", 0 TSRMLS_CC );
+ RETVAL_FALSE;
+ return;
+ }
+
+ fargs = safe_emalloc(count, sizeof(zval *), 0);
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(args), &pos);
+ for(i=0;i<count;i++) {
+ zval **val;
+ zend_hash_get_current_data_ex(Z_ARRVAL_P(args), (void **)&val, &pos);
+ fargs[i] = *val;
+ Z_ADDREF_P(fargs[i]);
+ /* TODO: needs refcount increase here? */
+ zend_hash_move_forward_ex(Z_ARRVAL_P(args), &pos);
+ }
+
+ umsg_format_helper(MSG_FORMAT_OBJECT(mfo), count, fargs, &formatted, &formatted_len, &INTL_DATA_ERROR_CODE(mfo) TSRMLS_CC);
+
+ for(i=0;i<count;i++) {
+ zval_ptr_dtor(&fargs[i]);
+ }
+
+ efree(fargs);
+
+ if (formatted && U_FAILURE( INTL_DATA_ERROR_CODE(mfo) ) ) {
+ efree(formatted);
+ }
+
+ INTL_METHOD_CHECK_STATUS( mfo, "Number formatting failed" );
+ INTL_METHOD_RETVAL_UTF8( mfo, formatted, formatted_len, 1 );
+}
+/* }}} */
+
+/* {{{ proto mixed MessageFormatter::format( array $args )
+ * Format a message. }}} */
+/* {{{ proto mixed msgfmt_format( MessageFormatter $nf, array $args )
+ * Format a message.
+ */
+PHP_FUNCTION( msgfmt_format )
+{
+ zval *args;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
+ &object, MessageFormatter_ce_ptr, &args ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ msgfmt_do_format(mfo, args, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto mixed MessageFormatter::formatMessage( string $locale, string $pattern, array $args )
+ * Format a message. }}} */
+/* {{{ proto mixed msgfmt_format_message( string $locale, string $pattern, array $args )
+ * Format a message.
+ */
+PHP_FUNCTION( msgfmt_format_message )
+{
+ zval *args;
+ UChar *spattern = NULL;
+ int spattern_len = 0;
+ char *pattern = NULL;
+ int pattern_len = 0;
+ char *slocale = NULL;
+ int slocale_len = 0;
+ MessageFormatter_object mf = {0};
+ MessageFormatter_object *mfo = &mf;
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "ssa",
+ &slocale, &slocale_len, &pattern, &pattern_len, &args ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ msgformat_data_init(&mfo->mf_data TSRMLS_CC);
+
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE(INTL_DATA_ERROR_CODE((mfo))) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_format_message: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(slocale_len == 0) {
+ slocale = INTL_G(default_locale);
+ }
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+ if(msgformat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_format_message: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+#endif
+
+ /* Create an ICU message formatter. */
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, slocale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ if(spattern && spattern_len) {
+ efree(spattern);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Creating message formatter failed");
+
+ msgfmt_do_format(mfo, args, return_value TSRMLS_CC);
+
+ /* drop the temporary formatter */
+ msgformat_data_free(&mfo->mf_data TSRMLS_CC);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/msgformat/msgformat_format.h b/ext/intl/msgformat/msgformat_format.h
new file mode 100644
index 0000000..b74deab
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_format.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_FORMAT_H
+#define MSG_FORMAT_FORMAT_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_format );
+PHP_FUNCTION( msgfmt_format_message );
+
+#endif // MSG_FORMAT_FORMAT_H
diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp
new file mode 100644
index 0000000..1895de2
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_helpers.cpp
@@ -0,0 +1,212 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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>
+#include <unicode/chariter.h>
+
+extern "C" {
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_format.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+}
+
+U_NAMESPACE_BEGIN
+/**
+ * This class isolates our access to private internal methods of
+ * MessageFormat. It is never instantiated; it exists only for C++
+ * access management.
+ */
+class MessageFormatAdapter {
+public:
+ static const Formattable::Type* getArgTypeList(const MessageFormat& m,
+ int32_t& count);
+};
+const Formattable::Type*
+MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
+ int32_t& count) {
+ return m.getArgTypeList(count);
+}
+U_NAMESPACE_END
+
+U_CFUNC int32_t umsg_format_arg_count(UMessageFormat *fmt)
+{
+ int32_t fmt_count = 0;
+ MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
+ return fmt_count;
+}
+
+U_CFUNC void umsg_format_helper(UMessageFormat *fmt, int arg_count, zval **args, UChar **formatted, int *formatted_len, UErrorCode *status TSRMLS_DC)
+{
+ int fmt_count = 0;
+ const Formattable::Type* argTypes =
+ MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
+ Formattable* fargs = new Formattable[fmt_count ? fmt_count : 1];
+
+ for(int32_t i = 0; i < fmt_count; ++i) {
+ UChar *stringVal = NULL;
+ int stringLen = 0;
+ int64_t tInt64 = 0;
+
+ switch(argTypes[i]) {
+ case Formattable::kDate:
+ convert_to_long_ex(&args[i]);
+ fargs[i].setDate(U_MILLIS_PER_SECOND * (double)Z_LVAL_P(args[i]));
+ break;
+
+ case Formattable::kDouble:
+ convert_to_double_ex(&args[i]);
+ fargs[i].setDouble(Z_DVAL_P(args[i]));
+ break;
+
+ case Formattable::kLong:
+ convert_to_long_ex(&args[i]);
+ fargs[i].setLong(Z_LVAL_P(args[i]));
+ break;
+
+ case Formattable::kInt64:
+ if(Z_TYPE_P(args[i]) == IS_DOUBLE) {
+ tInt64 = (int64_t)Z_DVAL_P(args[i]);
+ } else if(Z_TYPE_P(args[i]) == IS_LONG) {
+ tInt64 = (int64_t)Z_LVAL_P(args[i]);
+ } else {
+ SEPARATE_ZVAL_IF_NOT_REF(&args[i]);
+ convert_scalar_to_number( args[i] TSRMLS_CC );
+ tInt64 = (Z_TYPE_P(args[i]) == IS_DOUBLE)?(int64_t)Z_DVAL_P(args[i]):Z_LVAL_P(args[i]);
+ }
+ fargs[i].setInt64(tInt64);
+ break;
+
+ case Formattable::kString:
+ convert_to_string_ex(&args[i]);
+ intl_convert_utf8_to_utf16(&stringVal, &stringLen, Z_STRVAL_P(args[i]), Z_STRLEN_P(args[i]), status);
+ if(U_FAILURE(*status)){
+ delete[] fargs;
+ return;
+ }
+ fargs[i].setString(stringVal);
+ efree(stringVal);
+ break;
+
+ case Formattable::kArray:
+ case Formattable::kObject:
+ *status = U_UNSUPPORTED_ERROR;
+ delete[] fargs;
+ return;
+ }
+ }
+
+ UnicodeString resultStr;
+ FieldPosition fieldPosition(0);
+
+ /* format the message */
+ ((const MessageFormat*)fmt)->format(fargs, fmt_count, resultStr, fieldPosition, *status);
+
+ delete[] fargs;
+
+ if(U_FAILURE(*status)){
+ return;
+ }
+
+ *formatted_len = resultStr.length();
+ *formatted = eumalloc(*formatted_len+1);
+ resultStr.extract(*formatted, *formatted_len+1, *status);
+}
+
+#define cleanup_zvals() for(int j=i;j>=0;j--) { zval_ptr_dtor((*args)+i); }
+
+U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval ***args, UChar *source, int source_len, UErrorCode *status)
+{
+ UnicodeString srcString(source, source_len);
+ Formattable *fargs = ((const MessageFormat*)fmt)->parse(srcString, *count, *status);
+
+ if(U_FAILURE(*status)) {
+ return;
+ }
+
+ *args = (zval **)safe_emalloc(*count, sizeof(zval *), 0);
+
+ // assign formattables to varargs
+ for(int32_t i = 0; i < *count; i++) {
+ int64_t aInt64;
+ double aDate;
+ UnicodeString temp;
+ char *stmp;
+ int stmp_len;
+
+ ALLOC_INIT_ZVAL((*args)[i]);
+
+ switch(fargs[i].getType()) {
+ case Formattable::kDate:
+ aDate = ((double)fargs[i].getDate())/U_MILLIS_PER_SECOND;
+ if(aDate > LONG_MAX || aDate < -LONG_MAX) {
+ ZVAL_DOUBLE((*args)[i], aDate<0?ceil(aDate):floor(aDate));
+ } else {
+ ZVAL_LONG((*args)[i], (long)aDate);
+ }
+ break;
+
+ case Formattable::kDouble:
+ ZVAL_DOUBLE((*args)[i], (double)fargs[i].getDouble());
+ break;
+
+ case Formattable::kLong:
+ ZVAL_LONG((*args)[i], fargs[i].getLong());
+ break;
+
+ case Formattable::kInt64:
+ aInt64 = fargs[i].getInt64();
+ if(aInt64 > LONG_MAX || aInt64 < -LONG_MAX) {
+ ZVAL_DOUBLE((*args)[i], (double)aInt64);
+ } else {
+ ZVAL_LONG((*args)[i], (long)aInt64);
+ }
+ break;
+
+ case Formattable::kString:
+ fargs[i].getString(temp);
+ intl_convert_utf16_to_utf8(&stmp, &stmp_len, temp.getBuffer(), temp.length(), status);
+ if(U_FAILURE(*status)) {
+ cleanup_zvals();
+ return;
+ }
+ ZVAL_STRINGL((*args)[i], stmp, stmp_len, 0);
+ break;
+
+ case Formattable::kObject:
+ case Formattable::kArray:
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ cleanup_zvals();
+ break;
+ }
+ }
+ delete[] fargs;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/msgformat/msgformat_helpers.h b/ext/intl/msgformat/msgformat_helpers.h
new file mode 100644
index 0000000..30c7e39
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_helpers.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_HELPERS_H
+#define MSG_FORMAT_HELPERS_H
+
+int32_t umsg_format_arg_count(UMessageFormat *fmt);
+void umsg_format_helper(UMessageFormat *fmt, int arg_count, zval **args,
+ UChar **formatted, int *formatted_len, UErrorCode *status TSRMLS_DC);
+void umsg_parse_helper(UMessageFormat *fmt, int *count, zval ***args,
+ UChar *source, int source_len, UErrorCode *status);
+#endif // MSG_FORMAT_HELPERS_H
diff --git a/ext/intl/msgformat/msgformat_parse.c b/ext/intl/msgformat/msgformat_parse.c
new file mode 100644
index 0000000..f540b1d
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_parse.c
@@ -0,0 +1,161 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unicode/ustring.h>
+
+#include "php_intl.h"
+#include "msgformat_class.h"
+#include "msgformat_parse.h"
+#include "msgformat_data.h"
+#include "msgformat_helpers.h"
+#include "intl_convert.h"
+
+/* {{{ */
+static void msgfmt_do_parse(MessageFormatter_object *mfo, char *source, int src_len, zval *return_value TSRMLS_DC)
+{
+ zval **fargs;
+ int count = 0;
+ int i;
+ UChar *usource = NULL;
+ int usrc_len = 0;
+
+ intl_convert_utf8_to_utf16(&usource, &usrc_len, source, src_len, &INTL_DATA_ERROR_CODE(mfo));
+ INTL_METHOD_CHECK_STATUS(mfo, "Converting parse string failed");
+
+ umsg_parse_helper(MSG_FORMAT_OBJECT(mfo), &count, &fargs, usource, usrc_len, &INTL_DATA_ERROR_CODE(mfo));
+ if (usource) {
+ efree(usource);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Parsing failed");
+
+ array_init(return_value);
+ for(i=0;i<count;i++) {
+ add_next_index_zval(return_value, fargs[i]);
+ }
+ efree(fargs);
+}
+/* }}} */
+
+/* {{{ proto array MessageFormatter::parse( string $source )
+ * Parse a message }}} */
+/* {{{ proto array msgfmt_parse( MessageFormatter $nf, string $source )
+ * Parse a message.
+ */
+PHP_FUNCTION( msgfmt_parse )
+{
+ char *source;
+ int source_len;
+ MSG_FORMAT_METHOD_INIT_VARS;
+
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os",
+ &object, MessageFormatter_ce_ptr, &source, &source_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object. */
+ MSG_FORMAT_METHOD_FETCH_OBJECT;
+
+ msgfmt_do_parse(mfo, source, source_len, return_value TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto array MessageFormatter::formatMessage( string $locale, string $pattern, string $source )
+ * Parse a message. }}} */
+/* {{{ proto array numfmt_parse_message( string $locale, string $pattern, string $source )
+ * Parse a message.
+ */
+PHP_FUNCTION( msgfmt_parse_message )
+{
+ UChar *spattern = NULL;
+ int spattern_len = 0;
+ char *pattern = NULL;
+ int pattern_len = 0;
+ char *slocale = NULL;
+ int slocale_len = 0;
+ char *source = NULL;
+ int src_len = 0;
+ MessageFormatter_object mf = {0};
+ MessageFormatter_object *mfo = &mf;
+
+ /* Parse parameters. */
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sss",
+ &slocale, &slocale_len, &pattern, &pattern_len, &source, &src_len ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ msgformat_data_init(&mfo->mf_data TSRMLS_CC);
+
+ if(pattern && pattern_len) {
+ intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(mfo));
+ if( U_FAILURE(INTL_DATA_ERROR_CODE((mfo))) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "msgfmt_parse_message: error converting pattern to UTF-16", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+ } else {
+ spattern_len = 0;
+ spattern = NULL;
+ }
+
+ if(slocale_len == 0) {
+ slocale = INTL_G(default_locale);
+ }
+
+#ifdef MSG_FORMAT_QUOTE_APOS
+ if(msgformat_fix_quotes(&spattern, &spattern_len, &INTL_DATA_ERROR_CODE(mfo)) != SUCCESS) {
+ intl_error_set( NULL, U_INVALID_FORMAT_ERROR,
+ "msgfmt_parse_message: error converting pattern to quote-friendly format", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+#endif
+
+ /* Create an ICU message formatter. */
+ MSG_FORMAT_OBJECT(mfo) = umsg_open(spattern, spattern_len, slocale, NULL, &INTL_DATA_ERROR_CODE(mfo));
+ if(spattern && spattern_len) {
+ efree(spattern);
+ }
+ INTL_METHOD_CHECK_STATUS(mfo, "Creating message formatter failed");
+
+ msgfmt_do_parse(mfo, source, src_len, return_value TSRMLS_CC);
+
+ /* drop the temporary formatter */
+ msgformat_data_free(&mfo->mf_data TSRMLS_CC);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/msgformat/msgformat_parse.h b/ext/intl/msgformat/msgformat_parse.h
new file mode 100644
index 0000000..a937235
--- /dev/null
+++ b/ext/intl/msgformat/msgformat_parse.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Stanislav Malyshev <stas@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef MSG_FORMAT_PARSE_H
+#define MSG_FORMAT_PARSE_H
+
+#include <php.h>
+
+PHP_FUNCTION( msgfmt_parse );
+PHP_FUNCTION( msgfmt_parse_message );
+
+#endif // MSG_FORMAT_PARSE_H
diff --git a/ext/intl/normalizer/normalizer.c b/ext/intl/normalizer/normalizer.c
new file mode 100644
index 0000000..033b05c
--- /dev/null
+++ b/ext/intl/normalizer/normalizer.c
@@ -0,0 +1,68 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "normalizer_class.h"
+#include "normalizer.h"
+
+#include <unicode/utypes.h>
+#include <unicode/unorm.h>
+#include <unicode/ustring.h>
+
+/* {{{ normalizer_register_constants
+ * Register constants common for the both (OO and procedural)
+ * APIs.
+ */
+void normalizer_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Normalizer_ce_ptr )
+ {
+ zend_error( E_ERROR, "Normalizer class not defined" );
+ return;
+ }
+
+ #define NORMALIZER_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS)
+ #define NORMALIZER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Normalizer_ce_ptr, ZEND_STRS( #x ) - 1, NORMALIZER_##x TSRMLS_CC );
+ #define NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( Normalizer_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );
+
+ /* Normalization form constants */
+ NORMALIZER_EXPOSE_CLASS_CONST( NONE );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_D );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFD );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_KD );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFKD );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_C );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFC );
+ NORMALIZER_EXPOSE_CLASS_CONST( FORM_KC );
+ NORMALIZER_EXPOSE_CLASS_CONST( NFKC );
+
+ #undef NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_CONST
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/normalizer/normalizer.h b/ext/intl/normalizer/normalizer.h
new file mode 100644
index 0000000..eca9abe
--- /dev/null
+++ b/ext/intl/normalizer/normalizer.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef NORMALIZER_NORMALIZER_H
+#define NORMALIZER_NORMALIZER_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+#include <unicode/unorm.h>
+
+#define NORMALIZER_NONE UNORM_NONE
+#define NORMALIZER_FORM_D UNORM_NFD
+#define NORMALIZER_NFD UNORM_NFD
+#define NORMALIZER_FORM_KD UNORM_NFKD
+#define NORMALIZER_NFKD UNORM_NFKD
+#define NORMALIZER_FORM_C UNORM_NFC
+#define NORMALIZER_NFC UNORM_NFC
+#define NORMALIZER_FORM_KC UNORM_NFKC
+#define NORMALIZER_NFKC UNORM_NFKC
+#define NORMALIZER_DEFAULT UNORM_DEFAULT
+
+void normalizer_register_constants( INIT_FUNC_ARGS );
+
+#endif // NORMALIZER_NORMALIZER_H
diff --git a/ext/intl/normalizer/normalizer_class.c b/ext/intl/normalizer/normalizer_class.c
new file mode 100644
index 0000000..154d877
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_class.c
@@ -0,0 +1,81 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 */
+
+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
+ */
+
+zend_function_entry Normalizer_class_functions[] = {
+ ZEND_FENTRY( normalize, ZEND_FN( normalizer_normalize ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_FENTRY( isNormalized, ZEND_FN( normalizer_is_normalized ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ 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 100644
index 0000000..4d3f7d2
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_class.h
@@ -0,0 +1,43 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 );
+extern zend_class_entry *Normalizer_ce_ptr;
+#endif // #ifndef NORMALIZER_CLASS_H
diff --git a/ext/intl/normalizer/normalizer_normalize.c b/ext/intl/normalizer/normalizer_normalize.c
new file mode 100644
index 0000000..f46285e
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_normalize.c
@@ -0,0 +1,258 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "unicode/unorm.h"
+#include "normalizer.h"
+#include "normalizer_class.h"
+#include "normalizer_normalize.h"
+#include "intl_convert.h"
+
+/* {{{ proto string Normalizer::normalize( string $input [, string $form = FORM_C] )
+ * Normalize a string. }}} */
+/* {{{ proto string normalizer_normalize( string $input [, string $form = FORM_C] )
+ * Normalize a string.
+ */
+PHP_FUNCTION( normalizer_normalize )
+{
+ char* input = NULL;
+ /* form is optional, defaults to FORM_C */
+ long form = NORMALIZER_DEFAULT;
+ int input_len = 0;
+
+ UChar* uinput = NULL;
+ int uinput_len = 0;
+ int expansion_factor = 1;
+ UErrorCode status = U_ZERO_ERROR;
+
+ UChar* uret_buf = NULL;
+ int uret_len = 0;
+
+ char* ret_buf = NULL;
+ int32_t ret_len = 0;
+
+ int32_t size_needed;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "s|l",
+ &input, &input_len, &form ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_normalize: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ 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", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /*
+ * Normalize string (converting it to UTF-16 first).
+ */
+
+ /* First convert the string to UTF-16. */
+ intl_convert_utf8_to_utf16(&uinput, &uinput_len, input, input_len, &status );
+
+ if( U_FAILURE( status ) )
+ {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC );
+ if (uinput) {
+ efree( uinput );
+ }
+ RETURN_FALSE;
+ }
+
+
+ /* 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 occurred.
+ * (U_BUFFER_OVERFLOW_ERROR means that *target buffer is not large enough).
+ * (U_STRING_NOT_TERMINATED_WARNING usually means that the input string is empty).
+ */
+ if( U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR && status != U_STRING_NOT_TERMINATED_WARNING ) {
+ efree( uret_buf );
+ efree( uinput );
+ RETURN_NULL();
+ }
+
+ if ( size_needed > uret_len ) {
+ /* realloc does not seem to work properly - memory is corrupted
+ * uret_buf = eurealloc(uret_buf, size_needed + 1);
+ */
+ efree( uret_buf );
+ uret_buf = eumalloc( size_needed + 1 );
+ uret_len = size_needed;
+
+ status = U_ZERO_ERROR;
+
+ /* try normalize again */
+ size_needed = unorm_normalize( uinput, uinput_len, form, (int32_t) 0 /* options */, uret_buf, uret_len, &status);
+
+ /* Bail out if an unexpected error occurred. */
+ if( U_FAILURE(status) ) {
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL,"Error normalizing string", 0 TSRMLS_CC );
+ efree( uret_buf );
+ efree( uinput );
+ RETURN_FALSE;
+ }
+ }
+
+ efree( uinput );
+
+ /* the buffer we actually used */
+ uret_len = size_needed;
+
+ /* Convert normalized string from UTF-16 to UTF-8. */
+ intl_convert_utf16_to_utf8( &ret_buf, &ret_len, uret_buf, uret_len, &status );
+ efree( uret_buf );
+ if( U_FAILURE( status ) )
+ {
+ intl_error_set( NULL, status,
+ "normalizer_normalize: error converting normalized text UTF-8", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* Return it. */
+ RETVAL_STRINGL( ret_buf, ret_len, FALSE );
+}
+/* }}} */
+
+/* {{{ proto bool Normalizer::isNormalized( string $input [, string $form = FORM_C] )
+ * Test if a string is in a given normalization form. }}} */
+/* {{{ proto bool normalizer_is_normalize( string $input [, string $form = FORM_C] )
+ * Test if a string is in a given normalization form.
+ */
+PHP_FUNCTION( normalizer_is_normalized )
+{
+ char* input = NULL;
+ /* form is optional, defaults to FORM_C */
+ long form = NORMALIZER_DEFAULT;
+ int input_len = 0;
+
+ UChar* uinput = NULL;
+ int uinput_len = 0;
+ UErrorCode status = U_ZERO_ERROR;
+
+ UBool uret = FALSE;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ /* Parse parameters. */
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "s|l",
+ &input, &input_len, &form) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "normalizer_is_normalized: unable to parse input params", 0 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", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+
+ /*
+ * Test normalization of string (converting it to UTF-16 first).
+ */
+
+ /* First convert the string to UTF-16. */
+ intl_convert_utf8_to_utf16(&uinput, &uinput_len, input, input_len, &status );
+
+ if( U_FAILURE( status ) )
+ {
+ /* Set global error code. */
+ intl_error_set_code( NULL, status TSRMLS_CC );
+
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL, "Error converting string to UTF-16.", 0 TSRMLS_CC );
+ if (uinput) {
+ efree( uinput );
+ }
+ RETURN_FALSE;
+ }
+
+
+ /* test string */
+ uret = unorm_isNormalizedWithOptions( uinput, uinput_len, form, (int32_t) 0 /* options */, &status);
+
+ efree( uinput );
+
+ /* Bail out if an unexpected error occurred. */
+ if( U_FAILURE(status) ) {
+ /* Set error messages. */
+ intl_error_set_custom_msg( NULL,"Error testing if string is the given normalization form.", 0 TSRMLS_CC );
+ 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 100644
index 0000000..41c31f7
--- /dev/null
+++ b/ext/intl/normalizer/normalizer_normalize.h
@@ -0,0 +1,25 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Ed Batutis <ed@batutis.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef NORMALIZER_NORMALIZE_H
+#define NORMALIZER_NORMALIZE_H
+
+#include <php.h>
+
+PHP_FUNCTION( normalizer_normalize );
+PHP_FUNCTION( normalizer_is_normalized );
+
+#endif // NORMALIZER_NORMALIZE_H
diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c
new file mode 100644
index 0000000..90e3573
--- /dev/null
+++ b/ext/intl/php_intl.c
@@ -0,0 +1,733 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Vadim Savchuk <vsavchuk@productengine.com> |
+ | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Kirti Velankar <kirtig@yahoo-inc.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "php_intl.h"
+#include "intl_error.h"
+#include "collator/collator_class.h"
+#include "collator/collator.h"
+#include "collator/collator_attr.h"
+#include "collator/collator_compare.h"
+#include "collator/collator_sort.h"
+#include "collator/collator_convert.h"
+#include "collator/collator_locale.h"
+#include "collator/collator_create.h"
+#include "collator/collator_error.h"
+
+#include "formatter/formatter.h"
+#include "formatter/formatter_class.h"
+#include "formatter/formatter_attr.h"
+#include "formatter/formatter_format.h"
+#include "formatter/formatter_main.h"
+#include "formatter/formatter_parse.h"
+
+#include "msgformat/msgformat.h"
+#include "msgformat/msgformat_class.h"
+#include "msgformat/msgformat_attr.h"
+#include "msgformat/msgformat_format.h"
+#include "msgformat/msgformat_parse.h"
+
+#include "normalizer/normalizer.h"
+#include "normalizer/normalizer_class.h"
+#include "normalizer/normalizer_normalize.h"
+
+#include "locale/locale.h"
+#include "locale/locale_class.h"
+#include "locale/locale_methods.h"
+
+#include "dateformat/dateformat.h"
+#include "dateformat/dateformat_class.h"
+#include "dateformat/dateformat_attr.h"
+#include "dateformat/dateformat_format.h"
+#include "dateformat/dateformat_parse.h"
+#include "dateformat/dateformat_data.h"
+
+#include "resourcebundle/resourcebundle_class.h"
+
+#include "transliterator/transliterator.h"
+#include "transliterator/transliterator_class.h"
+#include "transliterator/transliterator_methods.h"
+
+#include "idn/idn.h"
+
+#if U_ICU_VERSION_MAJOR_NUM * 1000 + U_ICU_VERSION_MINOR_NUM >= 4002
+# include "spoofchecker/spoofchecker_class.h"
+# include "spoofchecker/spoofchecker.h"
+# include "spoofchecker/spoofchecker_create.h"
+# include "spoofchecker/spoofchecker_main.h"
+#endif
+
+#include "msgformat/msgformat.h"
+#include "common/common_error.h"
+
+#include <unicode/uloc.h>
+#include <ext/standard/info.h>
+
+#include "php_ini.h"
+#define INTL_MODULE_VERSION PHP_INTL_VERSION
+
+/*
+ * locale_get_default has a conflict since ICU also has
+ * a function with the same name
+ * in fact ICU appends the version no. to it also
+ * Hence the following undef for ICU version
+ * Same true for the locale_set_default function
+*/
+#undef locale_get_default
+#undef locale_set_default
+
+ZEND_DECLARE_MODULE_GLOBALS( intl )
+
+/* {{{ Arguments info */
+ZEND_BEGIN_ARG_INFO_EX(collator_static_0_args, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(collator_static_1_arg, 0, 0, 1)
+ ZEND_ARG_INFO(0, arg1)
+ZEND_END_ARG_INFO()
+
+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()
+
+ZEND_BEGIN_ARG_INFO_EX(collator_0_args, 0, 0, 1)
+ ZEND_ARG_OBJ_INFO(0, object, Collator, 0)
+ZEND_END_ARG_INFO()
+
+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()
+
+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()
+
+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()
+
+ZEND_BEGIN_ARG_INFO_EX(locale_0_args, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(locale_1_arg, 0, 0, 1)
+ ZEND_ARG_INFO(0, arg1)
+ZEND_END_ARG_INFO()
+
+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()
+
+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()
+
+ZEND_BEGIN_ARG_INFO_EX(locale_4_args, 0, 0, 4)
+ ZEND_ARG_INFO(0, arg1)
+ ZEND_ARG_INFO(0, arg2)
+ ZEND_ARG_INFO(0, arg3)
+ ZEND_ARG_INFO(0, arg4)
+ZEND_END_ARG_INFO()
+
+#define intl_0_args collator_static_0_args
+#define intl_1_arg collator_static_1_arg
+
+ZEND_BEGIN_ARG_INFO_EX(normalizer_args, 0, 0, 1)
+ ZEND_ARG_INFO(0, input)
+ ZEND_ARG_INFO(0, form)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(grapheme_1_arg, 0, 0, 1)
+ ZEND_ARG_INFO(0, string)
+ZEND_END_ARG_INFO()
+
+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()
+
+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()
+
+ZEND_BEGIN_ARG_INFO_EX(grapheme_strstr_args, 0, 0, 2)
+ ZEND_ARG_INFO(0, haystack)
+ ZEND_ARG_INFO(0, needle)
+ ZEND_ARG_INFO(0, before_needle)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(grapheme_extract_args, 0, 0, 2)
+ ZEND_ARG_INFO(0, arg1)
+ ZEND_ARG_INFO(0, arg2)
+ ZEND_ARG_INFO(0, arg3)
+ ZEND_ARG_INFO(0, arg4)
+ ZEND_ARG_INFO(1, arg5) /* 1 = pass by reference */
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(datefmt_parse_args, 0, 0, 2)
+ ZEND_ARG_INFO(0, formatter)
+ ZEND_ARG_INFO(0, string)
+ ZEND_ARG_INFO(1, position)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_create, 0, 0, 2)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, style)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_get_error_code, 0, 0, 1)
+ ZEND_ARG_INFO(0, nf)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_format, 0, 0, 2)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, num)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_format_currency, 0, 0, 3)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, num)
+ ZEND_ARG_INFO(0, currency)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_get_attribute, 0, 0, 2)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, attr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_set_attribute, 0, 0, 3)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, attr)
+ ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_set_symbol, 0, 0, 3)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, attr)
+ ZEND_ARG_INFO(0, symbol)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_set_pattern, 0, 0, 2)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_get_locale, 0, 0, 1)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_create, 0, 0, 2)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_get_error_code, 0, 0, 1)
+ ZEND_ARG_INFO(0, nf)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_get_error_message, 0, 0, 1)
+ ZEND_ARG_INFO(0, coll)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_format, 0, 0, 2)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_format_message, 0, 0, 3)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, pattern)
+ ZEND_ARG_INFO(0, args)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_parse, 0, 0, 2)
+ ZEND_ARG_INFO(0, nf)
+ ZEND_ARG_INFO(0, source)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_numfmt_parse_message, 0, 0, 3)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, pattern)
+ ZEND_ARG_INFO(0, source)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_set_pattern, 0, 0, 2)
+ ZEND_ARG_INFO(0, mf)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_msgfmt_get_locale, 0, 0, 1)
+ ZEND_ARG_INFO(0, mf)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_set_pattern, 0, 0, 2)
+ ZEND_ARG_INFO(0, mf)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_set_calendar, 0, 0, 2)
+ ZEND_ARG_INFO(0, mf)
+ ZEND_ARG_INFO(0, calendar)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_format, 0, 0, 0)
+ ZEND_ARG_INFO(0, args)
+ ZEND_ARG_INFO(0, array)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_datefmt_create, 0, 0, 3)
+ ZEND_ARG_INFO(0, locale)
+ ZEND_ARG_INFO(0, date_type)
+ ZEND_ARG_INFO(0, time_type)
+ ZEND_ARG_INFO(0, timezone_str)
+ ZEND_ARG_INFO(0, calendar)
+ ZEND_ARG_INFO(0, pattern)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_ascii, 0, 0, 1)
+ ZEND_ARG_INFO(0, domain)
+ ZEND_ARG_INFO(0, option)
+ ZEND_ARG_INFO(0, variant)
+ ZEND_ARG_INFO(1, idn_info)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_idn_to_utf8, 0, 0, 1)
+ ZEND_ARG_INFO(0, domain)
+ ZEND_ARG_INFO(0, option)
+ ZEND_ARG_INFO(0, variant)
+ ZEND_ARG_INFO(1, idn_info)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_create_proc, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, locale )
+ ZEND_ARG_INFO( 0, bundlename )
+ ZEND_ARG_INFO( 0, fallback )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_proc, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, bundle )
+ ZEND_ARG_INFO( 0, index )
+ ZEND_ARG_INFO( 0, fallback )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_count_proc, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, bundle )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_locales_proc, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, bundlename )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_error_code_proc, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, bundle )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_error_message_proc, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, bundle )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_void, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, id )
+ ZEND_ARG_INFO( 0, direction )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create_from_rules, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, rules )
+ ZEND_ARG_INFO( 0, direction )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_create_inverse, 0, 0, 1 )
+ ZEND_ARG_OBJ_INFO( 0, orig_trans, Transliterator, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_transliterate, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, trans )
+ ZEND_ARG_INFO( 0, subject )
+ ZEND_ARG_INFO( 0, start )
+ ZEND_ARG_INFO( 0, end )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( arginfo_transliterator_error, 0, 0, 1 )
+ ZEND_ARG_OBJ_INFO( 0, trans, Transliterator, 0 )
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ 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 )
+ PHP_FE( collator_get_sort_key, collator_2_args )
+
+ /* formatter functions */
+ PHP_FE( numfmt_create, arginfo_numfmt_create )
+ PHP_FE( numfmt_format, arginfo_numfmt_format )
+ PHP_FE( numfmt_parse, numfmt_parse_arginfo )
+ PHP_FE( numfmt_format_currency, arginfo_numfmt_format_currency )
+ PHP_FE( numfmt_parse_currency, numfmt_parse_currency_arginfo )
+ PHP_FE( numfmt_set_attribute, arginfo_numfmt_set_attribute )
+ PHP_FE( numfmt_get_attribute, arginfo_numfmt_get_attribute )
+ PHP_FE( numfmt_set_text_attribute, arginfo_numfmt_set_attribute )
+ PHP_FE( numfmt_get_text_attribute, arginfo_numfmt_get_attribute )
+ PHP_FE( numfmt_set_symbol, arginfo_numfmt_set_symbol )
+ PHP_FE( numfmt_get_symbol, arginfo_numfmt_get_attribute )
+ PHP_FE( numfmt_set_pattern, arginfo_numfmt_set_pattern )
+ PHP_FE( numfmt_get_pattern, arginfo_numfmt_get_error_code )
+ PHP_FE( numfmt_get_locale, arginfo_numfmt_get_locale )
+ PHP_FE( numfmt_get_error_code, arginfo_numfmt_get_error_code )
+ PHP_FE( numfmt_get_error_message, arginfo_numfmt_get_error_code )
+
+ /* normalizer functions */
+ PHP_FE( normalizer_normalize, normalizer_args )
+ PHP_FE( normalizer_is_normalized, normalizer_args )
+
+ /* Locale functions */
+ PHP_NAMED_FE( locale_get_default, zif_locale_get_default, locale_0_args )
+ PHP_NAMED_FE( locale_set_default, zif_locale_set_default, locale_1_arg )
+ PHP_FE( locale_get_primary_language, locale_1_arg )
+ PHP_FE( locale_get_script, locale_1_arg )
+ PHP_FE( locale_get_region, locale_1_arg )
+ PHP_FE( locale_get_keywords, locale_1_arg )
+ PHP_FE( locale_get_display_script, locale_2_args )
+ PHP_FE( locale_get_display_region, locale_2_args )
+ PHP_FE( locale_get_display_name, locale_2_args )
+ PHP_FE( locale_get_display_language, locale_2_args)
+ PHP_FE( locale_get_display_variant, locale_2_args )
+ PHP_FE( locale_compose, locale_1_arg )
+ PHP_FE( locale_parse, locale_1_arg )
+ PHP_FE( locale_get_all_variants, locale_1_arg )
+ PHP_FE( locale_filter_matches, locale_3_args )
+ PHP_FE( locale_canonicalize, locale_1_arg )
+ PHP_FE( locale_lookup, locale_4_args )
+ PHP_FE( locale_accept_from_http, locale_1_arg )
+
+ /* MessageFormatter functions */
+ PHP_FE( msgfmt_create, arginfo_msgfmt_create )
+ PHP_FE( msgfmt_format, arginfo_msgfmt_format )
+ PHP_FE( msgfmt_format_message, arginfo_msgfmt_format_message )
+ PHP_FE( msgfmt_parse, arginfo_msgfmt_parse )
+ PHP_FE( msgfmt_parse_message, arginfo_numfmt_parse_message )
+ PHP_FE( msgfmt_set_pattern, arginfo_msgfmt_set_pattern )
+ PHP_FE( msgfmt_get_pattern, arginfo_msgfmt_get_locale )
+ PHP_FE( msgfmt_get_locale, arginfo_msgfmt_get_locale )
+ PHP_FE( msgfmt_get_error_code, arginfo_msgfmt_get_error_code )
+ PHP_FE( msgfmt_get_error_message, arginfo_msgfmt_get_error_message )
+
+ /* IntlDateFormatter functions */
+ PHP_FE( datefmt_create, arginfo_datefmt_create )
+ PHP_FE( datefmt_get_datetype, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_get_timetype, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_get_calendar, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_set_calendar, arginfo_datefmt_set_calendar )
+ PHP_FE( datefmt_get_locale, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_get_timezone_id, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_set_timezone_id, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_get_pattern, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_set_pattern, arginfo_datefmt_set_pattern )
+ PHP_FE( datefmt_is_lenient, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_set_lenient, arginfo_msgfmt_get_locale )
+ PHP_FE( datefmt_format, arginfo_datefmt_format )
+ PHP_FE( datefmt_parse, datefmt_parse_args )
+ PHP_FE( datefmt_localtime , datefmt_parse_args )
+ PHP_FE( datefmt_get_error_code, arginfo_msgfmt_get_error_code )
+ PHP_FE( datefmt_get_error_message, arginfo_msgfmt_get_error_message )
+
+ /* 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 )
+
+ /* IDN functions */
+ PHP_FE( idn_to_ascii, arginfo_idn_to_ascii)
+ PHP_FE( idn_to_utf8, arginfo_idn_to_ascii)
+
+ /* ResourceBundle functions */
+ PHP_FE( resourcebundle_create, arginfo_resourcebundle_create_proc )
+ PHP_FE( resourcebundle_get, arginfo_resourcebundle_get_proc )
+ PHP_FE( resourcebundle_count, arginfo_resourcebundle_count_proc )
+ PHP_FE( resourcebundle_locales, arginfo_resourcebundle_locales_proc )
+ PHP_FE( resourcebundle_get_error_code, arginfo_resourcebundle_get_error_code_proc )
+ PHP_FE( resourcebundle_get_error_message, arginfo_resourcebundle_get_error_message_proc )
+
+ /* Transliterator functions */
+ PHP_FE( transliterator_create, arginfo_transliterator_create )
+ PHP_FE( transliterator_create_from_rules, arginfo_transliterator_create_from_rules )
+ PHP_FE( transliterator_list_ids, arginfo_transliterator_void )
+ PHP_FE( transliterator_create_inverse, arginfo_transliterator_create_inverse)
+ PHP_FE( transliterator_transliterate, arginfo_transliterator_transliterate )
+ PHP_FE( transliterator_get_error_code, arginfo_transliterator_error )
+ PHP_FE( transliterator_get_error_message, arginfo_transliterator_error )
+
+ /* common functions */
+ PHP_FE( intl_get_error_code, intl_0_args )
+ 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 )
+
+ PHP_FE_END
+};
+/* }}} */
+
+
+/* {{{ INI Settings */
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY(LOCALE_INI_NAME, NULL, PHP_INI_ALL, OnUpdateStringUnempty, default_locale, zend_intl_globals, intl_globals)
+ STD_PHP_INI_ENTRY("intl.error_level", "0", PHP_INI_ALL, OnUpdateLong, error_level, 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_STRING_CONSTANT("INTL_ICU_VERSION", U_ICU_VERSION, CONST_PERSISTENT | CONST_CS);
+#ifdef U_ICU_DATA_VERSION
+ REGISTER_STRING_CONSTANT("INTL_ICU_DATA_VERSION", U_ICU_DATA_VERSION, CONST_PERSISTENT | CONST_CS);
+#endif
+
+ /* Register 'Collator' PHP class */
+ collator_register_Collator_class( TSRMLS_C );
+
+ /* Expose Collator constants to PHP scripts */
+ collator_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Register 'NumberFormatter' PHP class */
+ formatter_register_class( TSRMLS_C );
+
+ /* Expose NumberFormatter constants to PHP scripts */
+ formatter_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Register 'Normalizer' PHP class */
+ normalizer_register_Normalizer_class( TSRMLS_C );
+
+ /* Expose Normalizer constants to PHP scripts */
+ normalizer_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Register 'Locale' PHP class */
+ locale_register_Locale_class( TSRMLS_C );
+
+ /* Expose Locale constants to PHP scripts */
+ locale_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ msgformat_register_class(TSRMLS_C);
+
+ grapheme_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Register 'DateFormat' PHP class */
+ dateformat_register_IntlDateFormatter_class( TSRMLS_C );
+
+ /* Expose DateFormat constants to PHP scripts */
+ dateformat_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Register 'ResourceBundle' PHP class */
+ resourcebundle_register_class( TSRMLS_C);
+
+ /* Register 'Transliterator' PHP class */
+ transliterator_register_Transliterator_class( TSRMLS_C );
+
+ /* Register Transliterator constants */
+ transliterator_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Expose ICU error codes to PHP scripts. */
+ intl_expose_icu_error_codes( INIT_FUNC_ARGS_PASSTHRU );
+
+ /* Expose IDN constants to PHP scripts. */
+ idn_register_constants(INIT_FUNC_ARGS_PASSTHRU);
+
+#if U_ICU_VERSION_MAJOR_NUM * 1000 + U_ICU_VERSION_MINOR_NUM >= 4002
+ /* Register 'Spoofchecker' PHP class */
+ spoofchecker_register_Spoofchecker_class( TSRMLS_C );
+
+ /* Expose Spoofchecker constants to PHP scripts */
+ spoofchecker_register_constants( INIT_FUNC_ARGS_PASSTHRU );
+#endif
+ /* Global error handling. */
+ intl_error_init( NULL TSRMLS_CC );
+
+ /* 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_row( 2, "ICU version", U_ICU_VERSION );
+#ifdef U_ICU_DATA_VERSION
+ php_info_print_table_row( 2, "ICU Data version", U_ICU_DATA_VERSION );
+#endif
+ 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 100644
index 0000000..4ede069
--- /dev/null
+++ b/ext/intl/php_intl.h
@@ -0,0 +1,77 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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;
+ long error_level;
+ZEND_END_MODULE_GLOBALS(intl)
+
+/* Macro to access request-wide global variables. */
+#ifdef ZTS
+#define INTL_G(v) TSRMG(intl_globals_id, zend_intl_globals *, v)
+#else
+#define INTL_G(v) (intl_globals.v)
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(intl)
+
+PHP_MINIT_FUNCTION(intl);
+PHP_MSHUTDOWN_FUNCTION(intl);
+PHP_RINIT_FUNCTION(intl);
+PHP_RSHUTDOWN_FUNCTION(intl);
+PHP_MINFO_FUNCTION(intl);
+
+#define PHP_INTL_VERSION "1.1.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/resourcebundle/TODO b/ext/intl/resourcebundle/TODO
new file mode 100644
index 0000000..ace4ceb
--- /dev/null
+++ b/ext/intl/resourcebundle/TODO
@@ -0,0 +1 @@
+- var_dump support
diff --git a/ext/intl/resourcebundle/resourcebundle.c b/ext/intl/resourcebundle/resourcebundle.c
new file mode 100644
index 0000000..6d39dfb
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle.c
@@ -0,0 +1,92 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <unicode/ures.h>
+
+#include <zend.h>
+#include <zend_API.h>
+
+#include "intl_convert.h"
+#include "intl_data.h"
+#include "resourcebundle/resourcebundle_class.h"
+
+/* {{{ ResourceBundle_extract_value */
+void resourcebundle_extract_value( zval *return_value, ResourceBundle_object *source TSRMLS_DC )
+{
+ UResType restype;
+ const UChar* ufield;
+ const uint8_t* bfield;
+ const int32_t* vfield;
+ int32_t ilen;
+ int i;
+ long lfield;
+ ResourceBundle_object* newrb;
+
+ restype = ures_getType( source->child );
+ switch (restype)
+ {
+ case URES_STRING:
+ ufield = ures_getString( source->child, &ilen, &INTL_DATA_ERROR_CODE(source) );
+ INTL_METHOD_CHECK_STATUS(source, "Failed to retrieve string value");
+ INTL_METHOD_RETVAL_UTF8(source, ufield, ilen, 0);
+ break;
+
+ case URES_BINARY:
+ bfield = ures_getBinary( source->child, &ilen, &INTL_DATA_ERROR_CODE(source) );
+ INTL_METHOD_CHECK_STATUS(source, "Failed to retrieve binary value");
+ ZVAL_STRINGL( return_value, (char *) bfield, ilen, 1 );
+ break;
+
+ case URES_INT:
+ lfield = ures_getInt( source->child, &INTL_DATA_ERROR_CODE(source) );
+ INTL_METHOD_CHECK_STATUS(source, "Failed to retrieve integer value");
+ ZVAL_LONG( return_value, lfield );
+ break;
+
+ case URES_INT_VECTOR:
+ vfield = ures_getIntVector( source->child, &ilen, &INTL_DATA_ERROR_CODE(source) );
+ INTL_METHOD_CHECK_STATUS(source, "Failed to retrieve vector value");
+ array_init( return_value );
+ for (i=0; i<ilen; i++) {
+ add_next_index_long( return_value, vfield[i] );
+ }
+ break;
+
+ case URES_ARRAY:
+ case URES_TABLE:
+ object_init_ex( return_value, ResourceBundle_ce_ptr );
+ newrb = (ResourceBundle_object *) zend_object_store_get_object( return_value TSRMLS_CC );
+ newrb->me = source->child;
+ source->child = NULL;
+ intl_errors_reset(INTL_DATA_ERROR_P(source) TSRMLS_CC);
+ break;
+
+ default:
+ intl_errors_set(INTL_DATA_ERROR_P(source), U_ILLEGAL_ARGUMENT_ERROR, "Unknown resource type", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ break;
+ }
+}
+/* }}} */
+
+/*
+ * 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/resourcebundle/resourcebundle.h b/ext/intl/resourcebundle/resourcebundle.h
new file mode 100644
index 0000000..607ece9
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle.h
@@ -0,0 +1,28 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef RESOURCEBUNDLE_H
+#define RESOURCEBUNDLE_H
+
+#include <unicode/ures.h>
+
+#include <zend.h>
+
+#include "resourcebundle/resourcebundle_class.h"
+
+void resourcebundle_extract_value( zval *target, ResourceBundle_object *source TSRMLS_DC);
+
+#endif // #ifndef RESOURCEBUNDLE_CLASS_H
diff --git a/ext/intl/resourcebundle/resourcebundle_class.c b/ext/intl/resourcebundle/resourcebundle_class.c
new file mode 100644
index 0000000..a6a73f5
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle_class.c
@@ -0,0 +1,458 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <stdlib.h>
+#include <unicode/ures.h>
+#include <unicode/uenum.h>
+
+#include <zend.h>
+#include <Zend/zend_exceptions.h>
+#include <Zend/zend_interfaces.h>
+#include <php.h>
+
+#include "php_intl.h"
+#include "intl_data.h"
+
+#include "resourcebundle/resourcebundle.h"
+#include "resourcebundle/resourcebundle_iterator.h"
+#include "resourcebundle/resourcebundle_class.h"
+
+zend_class_entry *ResourceBundle_ce_ptr = NULL;
+
+static zend_object_handlers ResourceBundle_object_handlers;
+
+/* {{{ ResourceBundle_object_dtor */
+static void ResourceBundle_object_destroy( void *object, zend_object_handle handle TSRMLS_DC )
+{
+ ResourceBundle_object *rb = (ResourceBundle_object *) object;
+
+ // only free local errors
+ intl_error_reset( INTL_DATA_ERROR_P(rb) TSRMLS_CC );
+
+ if (rb->me) {
+ ures_close( rb->me );
+ }
+ if (rb->child) {
+ ures_close( rb->child );
+ }
+
+ zend_object_std_dtor( object TSRMLS_CC );
+ efree(object);
+}
+/* }}} */
+
+/* {{{ ResourceBundle_object_create */
+static zend_object_value ResourceBundle_object_create( zend_class_entry *ce TSRMLS_DC )
+{
+ zend_object_value retval;
+ ResourceBundle_object *rb;
+
+ rb = ecalloc( 1, sizeof(ResourceBundle_object) );
+
+ zend_object_std_init( (zend_object *) rb, ce TSRMLS_CC );
+ object_properties_init((zend_object *) rb, ce);
+
+ intl_error_init( INTL_DATA_ERROR_P(rb) TSRMLS_CC );
+ rb->me = NULL;
+ rb->child = NULL;
+
+ retval.handlers = &ResourceBundle_object_handlers;
+ retval.handle = zend_objects_store_put( rb, ResourceBundle_object_destroy, NULL, NULL TSRMLS_CC );
+
+ return retval;
+}
+/* }}} */
+
+/* {{{ ResourceBundle_ctor */
+static void resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS)
+{
+ char * bundlename;
+ int bundlename_len = 0;
+ char * locale;
+ int locale_len = 0;
+ zend_bool fallback = 1;
+
+ char * pbuf;
+
+ zval *object = return_value;
+ ResourceBundle_object *rb = (ResourceBundle_object *) zend_object_store_get_object( object TSRMLS_CC);
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s!s!|b",
+ &locale, &locale_len, &bundlename, &bundlename_len, &fallback ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_ctor: unable to parse input parameters", 0 TSRMLS_CC );
+ zval_dtor( return_value );
+ RETURN_NULL();
+ }
+
+ INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value);
+
+ if (locale == NULL) {
+ locale = INTL_G(default_locale);
+ }
+
+ if (fallback) {
+ rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
+ } else {
+ rb->me = ures_openDirect(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
+ }
+
+ INTL_CTOR_CHECK_STATUS(rb, "resourcebundle_ctor: Cannot load libICU resource bundle");
+
+ if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING ||
+ INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
+ intl_errors_set_code(NULL, INTL_DATA_ERROR_CODE(rb) TSRMLS_CC);
+ spprintf(&pbuf, 0, "resourcebundle_ctor: Cannot load libICU resource "
+ "'%s' without fallback from %s to %s",
+ bundlename ? bundlename : "(default data)", locale,
+ ures_getLocaleByType(
+ rb->me, ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(rb)));
+ intl_errors_set_custom_msg(INTL_DATA_ERROR_P(rb), pbuf, 1 TSRMLS_CC);
+ efree(pbuf);
+ zval_dtor(return_value);
+ RETURN_NULL();
+ }
+}
+/* }}} */
+
+/* {{{ arginfo_resourcebundle__construct */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle___construct, 0, 0, 2 )
+ ZEND_ARG_INFO( 0, locale )
+ ZEND_ARG_INFO( 0, bundlename )
+ ZEND_ARG_INFO( 0, fallback )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto void ResourceBundle::__construct( string $locale [, string $bundlename [, bool $fallback = true ]] )
+ * ResourceBundle object constructor
+ */
+PHP_METHOD( ResourceBundle, __construct )
+{
+ return_value = getThis();
+ resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto ResourceBundle ResourceBundle::create( string $locale [, string $bundlename [, bool $fallback = true ]] )
+proto ResourceBundle resourcebundle_create( string $locale [, string $bundlename [, bool $fallback = true ]] )
+*/
+PHP_FUNCTION( resourcebundle_create )
+{
+ object_init_ex( return_value, ResourceBundle_ce_ptr );
+ resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ resourcebundle_array_fetch */
+static void resourcebundle_array_fetch(zval *object, zval *offset, zval *return_value, int fallback TSRMLS_DC)
+{
+ int32_t meindex;
+ char * mekey;
+ long mekeylen;
+ zend_bool is_numeric = 0;
+ char *pbuf;
+ ResourceBundle_object *rb;
+
+ intl_error_reset( NULL TSRMLS_CC );
+ RESOURCEBUNDLE_METHOD_FETCH_OBJECT;
+
+ if(Z_TYPE_P(offset) == IS_LONG) {
+ is_numeric = 1;
+ meindex = Z_LVAL_P(offset);
+ rb->child = ures_getByIndex( rb->me, meindex, rb->child, &INTL_DATA_ERROR_CODE(rb) );
+ } else if(Z_TYPE_P(offset) == IS_STRING) {
+ mekey = Z_STRVAL_P(offset);
+ mekeylen = Z_STRLEN_P(offset);
+ rb->child = ures_getByKey(rb->me, mekey, rb->child, &INTL_DATA_ERROR_CODE(rb) );
+ } else {
+ intl_errors_set(INTL_DATA_ERROR_P(rb), U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_get: index should be integer or string", 0 TSRMLS_CC);
+ RETURN_NULL();
+ }
+
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) TSRMLS_CC );
+ if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
+ if (is_numeric) {
+ spprintf( &pbuf, 0, "Cannot load resource element %d", meindex );
+ } else {
+ spprintf( &pbuf, 0, "Cannot load resource element '%s'", mekey );
+ }
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 TSRMLS_CC );
+ efree(pbuf);
+ RETURN_NULL();
+ }
+
+ if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
+ UErrorCode icuerror;
+ const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
+ if (is_numeric) {
+ spprintf( &pbuf, 0, "Cannot load element %d without fallback from to %s", meindex, locale );
+ } else {
+ spprintf( &pbuf, 0, "Cannot load element '%s' without fallback from to %s", mekey, locale );
+ }
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 TSRMLS_CC );
+ efree(pbuf);
+ RETURN_NULL();
+ }
+
+ resourcebundle_extract_value( return_value, rb TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ resourcebundle_array_get */
+zval *resourcebundle_array_get(zval *object, zval *offset, int type TSRMLS_DC)
+{
+ zval *retval;
+
+ if(offset == NULL) {
+ php_error( E_ERROR, "Cannot apply [] to ResourceBundle object" );
+ }
+ MAKE_STD_ZVAL(retval);
+
+ resourcebundle_array_fetch(object, offset, retval, 1 TSRMLS_CC);
+ Z_DELREF_P(retval);
+ return retval;
+}
+/* }}} */
+
+/* {{{ arginfo_resourcebundle_get */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, index )
+ ZEND_ARG_INFO( 0, fallback )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto mixed ResourceBundle::get( integer|string $resindex [, bool $fallback = true ] )
+ * proto mixed resourcebundle_get( ResourceBundle $rb, integer|string $resindex [, bool $fallback = true ] )
+ * Get resource identified by numerical index or key name.
+ */
+PHP_FUNCTION( resourcebundle_get )
+{
+ zend_bool fallback = 1;
+ zval * offset;
+ zval * object;
+
+ if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz|b", &object, ResourceBundle_ce_ptr, &offset, &fallback ) == FAILURE) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_get: unable to parse input params", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ }
+
+ resourcebundle_array_fetch(object, offset, return_value, fallback TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ resourcebundle_array_count */
+int resourcebundle_array_count(zval *object, long *count TSRMLS_DC)
+{
+ ResourceBundle_object *rb;
+ RESOURCEBUNDLE_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ if (rb->me == NULL) {
+ intl_errors_set(&rb->error, U_ILLEGAL_ARGUMENT_ERROR,
+ "Found unconstructed ResourceBundle", 0 TSRMLS_CC);
+ return 0;
+ }
+
+ *count = ures_getSize( rb->me );
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ arginfo_resourcebundle_count */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_count, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto int ResourceBundle::count()
+ * proto int resourcebundle_count( ResourceBundle $bundle )
+ * Get resources count
+ */
+PHP_FUNCTION( resourcebundle_count )
+{
+ int32_t len;
+ RESOURCEBUNDLE_METHOD_INIT_VARS;
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, ResourceBundle_ce_ptr ) == FAILURE ) {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_count: unable to parse input params", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ }
+
+ RESOURCEBUNDLE_METHOD_FETCH_OBJECT;
+
+ len = ures_getSize( rb->me );
+ RETURN_LONG( len );
+}
+
+/* {{{ arginfo_resourcebundle_getlocales */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_getlocales, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, bundlename )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto array ResourceBundle::getLocales( string $bundlename )
+ * proto array resourcebundle_locales( string $bundlename )
+ * Get available locales from ResourceBundle name
+ */
+PHP_FUNCTION( resourcebundle_locales )
+{
+ char * bundlename;
+ int bundlename_len = 0;
+ const char * entry;
+ int entry_len;
+ UEnumeration *icuenum;
+ UErrorCode icuerror = U_ZERO_ERROR;
+
+ intl_errors_reset( NULL TSRMLS_CC );
+
+ if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &bundlename, &bundlename_len ) == FAILURE )
+ {
+ intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_locales: unable to parse input params", 0 TSRMLS_CC);
+ RETURN_FALSE;
+ }
+
+ if(bundlename_len == 0) {
+ // fetch default locales list
+ bundlename = NULL;
+ }
+
+ icuenum = ures_openAvailableLocales( bundlename, &icuerror );
+ INTL_CHECK_STATUS(icuerror, "Cannot fetch locales list");
+
+ uenum_reset( icuenum, &icuerror );
+ INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list");
+
+ array_init( return_value );
+ while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) {
+ add_next_index_stringl( return_value, (char *) entry, entry_len, 1 );
+ }
+ uenum_close( icuenum );
+}
+/* }}} */
+
+/* {{{ arginfo_resourcebundle_get_error_code */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_error_code, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getErrorCode( )
+ * proto string resourcebundle_get_error_code( ResourceBundle $bundle )
+ * Get text description for ResourceBundle's last error code.
+ */
+PHP_FUNCTION( resourcebundle_get_error_code )
+{
+ RESOURCEBUNDLE_METHOD_INIT_VARS;
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, ResourceBundle_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ rb = (ResourceBundle_object *) zend_object_store_get_object( object TSRMLS_CC );
+
+ RETURN_LONG(INTL_DATA_ERROR_CODE(rb));
+}
+/* }}} */
+
+/* {{{ arginfo_resourcebundle_get_error_message */
+ZEND_BEGIN_ARG_INFO_EX( arginfo_resourcebundle_get_error_message, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getErrorMessage( )
+ * proto string resourcebundle_get_error_message( ResourceBundle $bundle )
+ * Get text description for ResourceBundle's last error.
+ */
+PHP_FUNCTION( resourcebundle_get_error_message )
+{
+ char* message = NULL;
+ RESOURCEBUNDLE_METHOD_INIT_VARS;
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, ResourceBundle_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "resourcebundle_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ rb = (ResourceBundle_object *) zend_object_store_get_object( object TSRMLS_CC );
+ message = (char *)intl_error_get_message(INTL_DATA_ERROR_P(rb) TSRMLS_CC);
+ RETURN_STRING(message, 0);
+}
+/* }}} */
+
+/* {{{ ResourceBundle_class_functions
+ * Every 'ResourceBundle' class method has an entry in this table
+ */
+static zend_function_entry ResourceBundle_class_functions[] = {
+ PHP_ME( ResourceBundle, __construct, arginfo_resourcebundle___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR )
+ ZEND_NAMED_ME( create, ZEND_FN( resourcebundle_create ), arginfo_resourcebundle___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC )
+ ZEND_NAMED_ME( get, ZEND_FN(resourcebundle_get), arginfo_resourcebundle_get, ZEND_ACC_PUBLIC )
+ ZEND_NAMED_ME( count, ZEND_FN(resourcebundle_count), arginfo_resourcebundle_count, ZEND_ACC_PUBLIC )
+ ZEND_NAMED_ME( getLocales, ZEND_FN(resourcebundle_locales), arginfo_resourcebundle_getlocales, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC )
+ ZEND_NAMED_ME( getErrorCode, ZEND_FN(resourcebundle_get_error_code), arginfo_resourcebundle_get_error_code, ZEND_ACC_PUBLIC )
+ ZEND_NAMED_ME( getErrorMessage, ZEND_FN(resourcebundle_get_error_message), arginfo_resourcebundle_get_error_message, ZEND_ACC_PUBLIC )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ resourcebundle_register_class
+ * Initialize 'ResourceBundle' class
+ */
+void resourcebundle_register_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ INIT_CLASS_ENTRY( ce, "ResourceBundle", ResourceBundle_class_functions );
+
+ ce.create_object = ResourceBundle_object_create;
+ ce.get_iterator = resourcebundle_get_iterator;
+
+ ResourceBundle_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+
+ if( !ResourceBundle_ce_ptr )
+ {
+ zend_error(E_ERROR, "Failed to register ResourceBundle class");
+ return;
+ }
+
+ ResourceBundle_object_handlers = std_object_handlers;
+ ResourceBundle_object_handlers.clone_obj = NULL; /* ICU ResourceBundle has no clone implementation */
+ ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get;
+ ResourceBundle_object_handlers.count_elements = resourcebundle_array_count;
+
+ zend_class_implements(ResourceBundle_ce_ptr TSRMLS_CC, 1, zend_ce_traversable);
+}
+/* }}} */
+
+/*
+ * 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/resourcebundle/resourcebundle_class.h b/ext/intl/resourcebundle/resourcebundle_class.h
new file mode 100644
index 0000000..8da3ed9
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle_class.h
@@ -0,0 +1,58 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef RESOURCEBUNDLE_CLASS_H
+#define RESOURCEBUNDLE_CLASS_H
+
+#include <unicode/ures.h>
+
+#include <zend.h>
+#include "php.h"
+
+#include "intl_error.h"
+
+typedef struct {
+ zend_object zend;
+ intl_error error;
+
+ UResourceBundle *me;
+ UResourceBundle *child;
+} ResourceBundle_object;
+
+#define RESOURCEBUNDLE_METHOD_INIT_VARS INTL_METHOD_INIT_VARS(ResourceBundle, rb)
+#define RESOURCEBUNDLE_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(ResourceBundle, rb)
+#define RESOURCEBUNDLE_METHOD_FETCH_OBJECT \
+ INTL_METHOD_FETCH_OBJECT(ResourceBundle, rb); \
+ if (RESOURCEBUNDLE_OBJECT(rb) == NULL) { \
+ intl_errors_set(&rb->error, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Found unconstructed ResourceBundle", 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ }
+
+
+#define RESOURCEBUNDLE_OBJECT(rb) (rb)->me
+
+void resourcebundle_register_class( TSRMLS_D );
+extern zend_class_entry *ResourceBundle_ce_ptr;
+
+PHP_FUNCTION( resourcebundle_create );
+PHP_FUNCTION( resourcebundle_get );
+PHP_FUNCTION( resourcebundle_count );
+PHP_FUNCTION( resourcebundle_locales );
+PHP_FUNCTION( resourcebundle_get_error_code );
+PHP_FUNCTION( resourcebundle_get_error_message );
+
+#endif // #ifndef RESOURCEBUNDLE_CLASS_H
diff --git a/ext/intl/resourcebundle/resourcebundle_iterator.c b/ext/intl/resourcebundle/resourcebundle_iterator.c
new file mode 100644
index 0000000..16e1b92
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle_iterator.c
@@ -0,0 +1,192 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#include <php.h>
+#include <zend.h>
+#include <zend_API.h>
+
+#include "resourcebundle/resourcebundle.h"
+#include "resourcebundle/resourcebundle_class.h"
+#include "resourcebundle/resourcebundle_iterator.h"
+
+/*
+ * Although libicu offers iterator functions, they are not used here: libicu does iterate
+ * irrespective of array indices. Those cannot be recreated afterwards. Arrays as well as tables
+ * can however be accessed by numerical index, with table keys readable ex post.
+ */
+
+/* {{{ resourcebundle_iterator_read */
+static void resourcebundle_iterator_read( ResourceBundle_iterator *iterator TSRMLS_DC )
+{
+ UErrorCode icuerror = U_ZERO_ERROR;
+ ResourceBundle_object *rb = iterator->subject;
+
+ rb->child = ures_getByIndex( rb->me, iterator->i, rb->child, &icuerror );
+
+ if (U_SUCCESS(icuerror)) {
+ /* ATTN: key extraction must be the first thing to do... rb->child might be reset in read! */
+ if (iterator->is_table) {
+ iterator->currentkey = estrdup( ures_getKey( rb->child ) );
+ }
+ MAKE_STD_ZVAL( iterator->current );
+ resourcebundle_extract_value( iterator->current, rb TSRMLS_CC );
+ }
+ else {
+ // zend_throw_exception( spl_ce_OutOfRangeException, "Running past end of ResourceBundle", 0 TSRMLS_CC);
+ iterator->current = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_invalidate */
+static void resourcebundle_iterator_invalidate( zend_object_iterator *iter TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+
+ if (iterator->current) {
+ zval_ptr_dtor( &iterator->current );
+ iterator->current = NULL;
+ }
+ if (iterator->currentkey) {
+ efree( iterator->currentkey );
+ iterator->currentkey = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_dtor */
+static void resourcebundle_iterator_dtor( zend_object_iterator *iter TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+ zval *object = (zval *)iterator->intern.data;
+
+ resourcebundle_iterator_invalidate( iter TSRMLS_CC );
+
+ Z_DELREF_P(object);
+
+ efree(iterator);
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_has_more */
+static int resourcebundle_iterator_has_more( zend_object_iterator *iter TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+ return (iterator->i < iterator->length) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_current */
+static void resourcebundle_iterator_current( zend_object_iterator *iter, zval ***data TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+ if (!iterator->current) {
+ resourcebundle_iterator_read( iterator TSRMLS_CC);
+ }
+ *data = &iterator->current;
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_key */
+static int resourcebundle_iterator_key( zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+
+ if (!iterator->current) {
+ resourcebundle_iterator_read( iterator TSRMLS_CC);
+ }
+ if (iterator->is_table) {
+ *str_key = estrdup( iterator->currentkey );
+ *str_key_len = strlen( iterator->currentkey ) + 1;
+ return HASH_KEY_IS_STRING;
+ }
+ else {
+ *int_key = iterator->i;
+ return HASH_KEY_IS_LONG;
+ }
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_has_more */
+static void resourcebundle_iterator_step( zend_object_iterator *iter TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+
+ iterator->i++;
+ resourcebundle_iterator_invalidate( iter TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_has_reset */
+static void resourcebundle_iterator_reset( zend_object_iterator *iter TSRMLS_DC )
+{
+ ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter;
+
+ iterator->i = 0;
+ resourcebundle_iterator_invalidate( iter TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ resourcebundle_iterator_funcs */
+static zend_object_iterator_funcs resourcebundle_iterator_funcs = {
+ resourcebundle_iterator_dtor,
+ resourcebundle_iterator_has_more,
+ resourcebundle_iterator_current,
+ resourcebundle_iterator_key,
+ resourcebundle_iterator_step,
+ resourcebundle_iterator_reset,
+ resourcebundle_iterator_invalidate
+};
+/* }}} */
+
+/* {{{ resourcebundle_get_iterator */
+zend_object_iterator *resourcebundle_get_iterator( zend_class_entry *ce, zval *object, int byref TSRMLS_DC )
+{
+ ResourceBundle_object *rb = (ResourceBundle_object *) zend_object_store_get_object( object TSRMLS_CC );
+ ResourceBundle_iterator *iterator = emalloc( sizeof( ResourceBundle_iterator ) );
+
+ if (byref) {
+ php_error( E_ERROR, "ResourceBundle does not support writable iterators" );
+ }
+
+ Z_ADDREF_P(object);
+ iterator->intern.data = (void *) object;
+ iterator->intern.funcs = &resourcebundle_iterator_funcs;
+
+ iterator->subject = rb;
+
+ /* The iterated rb can only be either URES_TABLE or URES_ARRAY
+ * All other types are returned as php primitives!
+ */
+ iterator->is_table = (ures_getType( rb->me ) == URES_TABLE);
+ iterator->length = ures_getSize( rb->me );
+
+ iterator->current = NULL;
+ iterator->currentkey = NULL;
+ iterator->i = 0;
+
+ return (zend_object_iterator *) iterator;
+}
+/* }}} */
+
+/*
+ * 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/resourcebundle/resourcebundle_iterator.h b/ext/intl/resourcebundle/resourcebundle_iterator.h
new file mode 100644
index 0000000..90dba86
--- /dev/null
+++ b/ext/intl/resourcebundle/resourcebundle_iterator.h
@@ -0,0 +1,36 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef RESOURCEBUNDLE_ITERATOR_H
+#define RESOURCEBUNDLE_ITERATOR_H
+
+#include <zend.h>
+
+#include "resourcebundle/resourcebundle_class.h"
+
+typedef struct {
+ zend_object_iterator intern;
+ ResourceBundle_object *subject;
+ zend_bool is_table;
+ long length;
+ zval *current;
+ char *currentkey;
+ long i;
+} ResourceBundle_iterator;
+
+zend_object_iterator *resourcebundle_get_iterator( zend_class_entry *ce, zval *object, int byref TSRMLS_DC );
+
+#endif // #ifndef RESOURCEBUNDLE_ITERATOR_H
diff --git a/ext/intl/spoofchecker/spoofchecker.c b/ext/intl/spoofchecker/spoofchecker.c
new file mode 100644
index 0000000..42a014a
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker.c
@@ -0,0 +1,60 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "spoofchecker_class.h"
+#include "spoofchecker.h"
+
+#include <unicode/uspoof.h>
+
+
+/* {{{ spoofchecker_register_constants
+ * Register constants
+ */
+void spoofchecker_register_constants(INIT_FUNC_ARGS)
+{
+ if (!Spoofchecker_ce_ptr)
+ {
+ zend_error(E_ERROR, "Spoofchecker class not defined");
+ return;
+ }
+
+ #define SPOOFCHECKER_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long(Spoofchecker_ce_ptr, ZEND_STRS( #x ) - 1, USPOOF_##x TSRMLS_CC);
+
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(SINGLE_SCRIPT_CONFUSABLE)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(MIXED_SCRIPT_CONFUSABLE)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(WHOLE_SCRIPT_CONFUSABLE)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(ANY_CASE)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(SINGLE_SCRIPT)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(INVISIBLE)
+ SPOOFCHECKER_EXPOSE_CLASS_CONST(CHAR_LIMIT)
+
+
+ #undef SPOOFCHECKER_EXPOSE_CLASS_CONST
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/spoofchecker/spoofchecker.h b/ext/intl/spoofchecker/spoofchecker.h
new file mode 100644
index 0000000..f976d63
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef SPOOFCHECKER_SPOOFCHECKER_H
+#define SPOOFCHECKER_SPOOFCHECKER_H
+
+#include <php.h>
+
+void spoofchecker_register_constants(INIT_FUNC_ARGS);
+
+#endif // SPOOFCHECKER_SPOOFCHECKER_H
diff --git a/ext/intl/spoofchecker/spoofchecker_class.c b/ext/intl/spoofchecker/spoofchecker_class.c
new file mode 100644
index 0000000..6c2b790
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_class.c
@@ -0,0 +1,210 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "spoofchecker_class.h"
+#include "spoofchecker_main.h"
+#include "spoofchecker_create.h"
+#include "php_intl.h"
+#include "intl_error.h"
+
+#include <unicode/uspoof.h>
+
+zend_class_entry *Spoofchecker_ce_ptr = NULL;
+static zend_object_handlers Spoofchecker_handlers;
+
+/*
+ * Auxiliary functions needed by objects of 'Spoofchecker' class
+ */
+
+/* {{{ Spoofchecker_objects_dtor */
+static void Spoofchecker_objects_dtor(
+ void *object,
+ zend_object_handle handle TSRMLS_DC)
+{
+ zend_objects_destroy_object(object, handle TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ Spoofchecker_objects_free */
+void Spoofchecker_objects_free(zend_object *object TSRMLS_DC)
+{
+ Spoofchecker_object* co = (Spoofchecker_object*)object;
+
+ zend_object_std_dtor(&co->zo TSRMLS_CC);
+
+ spoofchecker_object_destroy(co TSRMLS_CC);
+
+ efree(co);
+}
+/* }}} */
+
+/* {{{ Spoofchecker_object_create */
+zend_object_value Spoofchecker_object_create(
+ zend_class_entry *ce TSRMLS_DC)
+{
+ zend_object_value retval;
+ Spoofchecker_object* intern;
+
+ intern = ecalloc(1, sizeof(Spoofchecker_object));
+ intl_error_init(SPOOFCHECKER_ERROR_P(intern) TSRMLS_CC);
+ zend_object_std_init(&intern->zo, ce TSRMLS_CC);
+ object_properties_init(&intern->zo, ce);
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ Spoofchecker_objects_dtor,
+ (zend_objects_free_object_storage_t)Spoofchecker_objects_free,
+ NULL TSRMLS_CC);
+
+ retval.handlers = &Spoofchecker_handlers;
+
+ return retval;
+}
+/* }}} */
+
+/*
+ * 'Spoofchecker' class registration structures & functions
+ */
+
+/* {{{ Spoofchecker methods arguments info */
+ZEND_BEGIN_ARG_INFO_EX(spoofchecker_0_args, 0, 0, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(spoofchecker_set_checks, 0, 0, 1)
+ ZEND_ARG_INFO(0, checks)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(spoofchecker_set_allowed_locales, 0, 0, 1)
+ ZEND_ARG_INFO(0, locale_list)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(spoofchecker_is_suspicous, 0, 0, 1)
+ ZEND_ARG_INFO(0, text)
+ ZEND_ARG_INFO(1, error)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(spoofchecker_are_confusable, 0, 0, 2)
+ ZEND_ARG_INFO(0, s1)
+ ZEND_ARG_INFO(0, s2)
+ ZEND_ARG_INFO(1, error)
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ Spoofchecker_class_functions
+ * Every 'Spoofchecker' class method has an entry in this table
+ */
+
+zend_function_entry Spoofchecker_class_functions[] = {
+ PHP_ME(Spoofchecker, __construct, spoofchecker_0_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
+ PHP_ME(Spoofchecker, isSuspicious, spoofchecker_is_suspicous, ZEND_ACC_PUBLIC)
+ PHP_ME(Spoofchecker, areConfusable, spoofchecker_are_confusable, ZEND_ACC_PUBLIC)
+ PHP_ME(Spoofchecker, setAllowedLocales, spoofchecker_set_allowed_locales, ZEND_ACC_PUBLIC)
+ PHP_ME(Spoofchecker, setChecks, spoofchecker_set_checks, ZEND_ACC_PUBLIC)
+ PHP_FE_END
+};
+/* }}} */
+
+static zend_object_value spoofchecker_clone_obj(zval *object TSRMLS_DC) /* {{{ */
+{
+ zend_object_value new_obj_val;
+ zend_object_handle handle = Z_OBJ_HANDLE_P(object);
+ Spoofchecker_object *sfo, *new_sfo;
+
+ sfo = (Spoofchecker_object *) zend_object_store_get_object(object TSRMLS_CC);
+ intl_error_reset(SPOOFCHECKER_ERROR_P(sfo) TSRMLS_CC);
+
+ new_obj_val = Spoofchecker_ce_ptr->create_object(Z_OBJCE_P(object) TSRMLS_CC);
+ new_sfo = (Spoofchecker_object *)zend_object_store_get_object_by_handle(new_obj_val.handle TSRMLS_CC);
+ /* clone standard parts */
+ zend_objects_clone_members(&new_sfo->zo, new_obj_val, &sfo->zo, handle TSRMLS_CC);
+ /* clone internal object */
+ new_sfo->uspoof = uspoof_clone(sfo->uspoof, SPOOFCHECKER_ERROR_CODE_P(new_sfo));
+ if(U_FAILURE(SPOOFCHECKER_ERROR_CODE(new_sfo))) {
+ /* set up error in case error handler is interested */
+ intl_error_set( NULL, SPOOFCHECKER_ERROR_CODE(new_sfo), "Failed to clone SpoofChecker object", 0 TSRMLS_CC );
+ Spoofchecker_objects_dtor(new_sfo, new_obj_val.handle TSRMLS_CC); /* free new object */
+ zend_error(E_ERROR, "Failed to clone SpoofChecker object");
+ }
+ return new_obj_val;
+}
+/* }}} */
+
+/* {{{ spoofchecker_register_Spoofchecker_class
+ * Initialize 'Spoofchecker' class
+ */
+void spoofchecker_register_Spoofchecker_class(TSRMLS_D)
+{
+ zend_class_entry ce;
+
+ /* Create and register 'Spoofchecker' class. */
+ INIT_CLASS_ENTRY(ce, "Spoofchecker", Spoofchecker_class_functions);
+ ce.create_object = Spoofchecker_object_create;
+ Spoofchecker_ce_ptr = zend_register_internal_class(&ce TSRMLS_CC);
+
+ memcpy(&Spoofchecker_handlers, zend_get_std_object_handlers(),
+ sizeof Spoofchecker_handlers);
+ Spoofchecker_handlers.clone_obj = spoofchecker_clone_obj;
+
+ if (!Spoofchecker_ce_ptr) {
+ zend_error(E_ERROR,
+ "Spoofchecker: attempt to create properties "
+ "on a non-registered class.");
+ return;
+ }
+}
+/* }}} */
+
+/* {{{ void spoofchecker_object_init( Spoofchecker_object* co )
+ * Initialize internals of Spoofchecker_object.
+ * Must be called before any other call to 'spoofchecker_object_...' functions.
+ */
+void spoofchecker_object_init(Spoofchecker_object* co TSRMLS_DC)
+{
+ if (!co) {
+ return;
+ }
+
+ intl_error_init(SPOOFCHECKER_ERROR_P(co) TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ void spoofchecker_object_destroy( Spoofchecker_object* co )
+ * Clean up mem allocted by internals of Spoofchecker_object
+ */
+void spoofchecker_object_destroy(Spoofchecker_object* co TSRMLS_DC)
+{
+ if (!co) {
+ return;
+ }
+
+ if (co->uspoof) {
+ uspoof_close(co->uspoof);
+ co->uspoof = NULL;
+ }
+
+ intl_error_reset(SPOOFCHECKER_ERROR_P(co) TSRMLS_CC);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/spoofchecker/spoofchecker_class.h b/ext/intl/spoofchecker/spoofchecker_class.h
new file mode 100644
index 0000000..ec043d9
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_class.h
@@ -0,0 +1,76 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef SPOOFCHECKER_CLASS_H
+#define SPOOFCHECKER_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "spoofchecker_create.h"
+#include "intl_error.h"
+#include "intl_data.h"
+
+#include <unicode/uspoof.h>
+
+typedef struct {
+ zend_object zo;
+
+ // error handling
+ intl_error err;
+
+ // ICU Spoofchecker
+ USpoofChecker* uspoof;
+} Spoofchecker_object;
+
+#define SPOOFCHECKER_ERROR(co) (co)->err
+#define SPOOFCHECKER_ERROR_P(co) &(SPOOFCHECKER_ERROR(co))
+
+#define SPOOFCHECKER_ERROR_CODE(co) INTL_ERROR_CODE(SPOOFCHECKER_ERROR(co))
+#define SPOOFCHECKER_ERROR_CODE_P(co) &(INTL_ERROR_CODE(SPOOFCHECKER_ERROR(co)))
+
+void spoofchecker_register_Spoofchecker_class(TSRMLS_D);
+
+void spoofchecker_object_init(Spoofchecker_object* co TSRMLS_DC);
+void spoofchecker_object_destroy(Spoofchecker_object* co TSRMLS_DC);
+
+extern zend_class_entry *Spoofchecker_ce_ptr;
+
+/* Auxiliary macros */
+
+#define SPOOFCHECKER_METHOD_INIT_VARS \
+ zval* object = getThis(); \
+ Spoofchecker_object* co = NULL; \
+ intl_error_reset(NULL TSRMLS_CC); \
+
+#define SPOOFCHECKER_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT(Spoofchecker, co)
+#define SPOOFCHECKER_METHOD_FETCH_OBJECT \
+ SPOOFCHECKER_METHOD_FETCH_OBJECT_NO_CHECK; \
+ if (co->uspoof == NULL) { \
+ intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR, \
+ "Found unconstructed Spoofchecker", 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ }
+
+// Macro to check return value of a ucol_* function call.
+#define SPOOFCHECKER_CHECK_STATUS(co, msg) \
+ intl_error_set_code(NULL, SPOOFCHECKER_ERROR_CODE(co) TSRMLS_CC); \
+ if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) { \
+ intl_errors_set_custom_msg(SPOOFCHECKER_ERROR_P(co), msg, 0 TSRMLS_CC); \
+ RETURN_FALSE; \
+ } \
+
+#endif // #ifndef SPOOFCHECKER_CLASS_H
diff --git a/ext/intl/spoofchecker/spoofchecker_create.c b/ext/intl/spoofchecker/spoofchecker_create.c
new file mode 100644
index 0000000..cf0173f
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_create.c
@@ -0,0 +1,59 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "spoofchecker_class.h"
+#include "spoofchecker_create.h"
+#include "intl_data.h"
+
+/* {{{ proto Spoofchecker Spoofchecker::__construct()
+ * Spoofchecker object constructor.
+ */
+PHP_METHOD(Spoofchecker, __construct)
+{
+ int checks;
+ SPOOFCHECKER_METHOD_INIT_VARS;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ SPOOFCHECKER_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ co->uspoof = uspoof_open(SPOOFCHECKER_ERROR_CODE_P(co));
+ INTL_CTOR_CHECK_STATUS(co, "spoofchecker: unable to open ICU Spoof Checker");
+
+ /* Single-script enforcement is on by default. This fails for languages
+ like Japanese that legally use multiple scripts within a single word,
+ so we turn it off.
+ */
+ checks = uspoof_getChecks(co->uspoof, SPOOFCHECKER_ERROR_CODE_P(co));
+ uspoof_setChecks(co->uspoof, checks & ~USPOOF_SINGLE_SCRIPT, SPOOFCHECKER_ERROR_CODE_P(co));
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/spoofchecker/spoofchecker_create.h b/ext/intl/spoofchecker/spoofchecker_create.h
new file mode 100644
index 0000000..313faab
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_create.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef SPOOFCHECKER_CREATE_H
+#define SPOOFCHECKER_CREATE_H
+
+#include <php.h>
+
+PHP_METHOD(Spoofchecker, __construct);
+
+#endif // SPOOFCHECKER_CREATE_H
diff --git a/ext/intl/spoofchecker/spoofchecker_main.c b/ext/intl/spoofchecker/spoofchecker_main.c
new file mode 100644
index 0000000..c37b918
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_main.c
@@ -0,0 +1,142 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "spoofchecker_class.h"
+
+/* {{{ proto bool Spoofchecker::isSuspicious( string text[, int &error_code ] )
+ * Checks if a given text contains any suspicious characters
+ */
+PHP_METHOD(Spoofchecker, isSuspicious)
+{
+ int ret;
+ char *text;
+ int text_len;
+ zval *error_code = NULL;
+ SPOOFCHECKER_METHOD_INIT_VARS;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &text, &text_len, &error_code)) {
+ return;
+ }
+
+ SPOOFCHECKER_METHOD_FETCH_OBJECT;
+
+ ret = uspoof_checkUTF8(co->uspoof, text, text_len, NULL, SPOOFCHECKER_ERROR_CODE_P(co));
+
+ if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
+ RETURN_TRUE;
+ }
+
+ if (error_code) {
+ zval_dtor(error_code);
+ ZVAL_LONG(error_code, ret);
+ }
+ RETVAL_BOOL(ret != 0);
+}
+/* }}} */
+
+/* {{{ proto bool Spoofchecker::areConfusable( string str1, string str2[, int &error_code ] )
+ * Checks if a given text contains any confusable characters
+ */
+PHP_METHOD(Spoofchecker, areConfusable)
+{
+ int ret;
+ char *s1, *s2;
+ int s1_len, s2_len;
+ zval *error_code = NULL;
+ SPOOFCHECKER_METHOD_INIT_VARS;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|z", &s1, &s1_len,
+ &s2, &s2_len, &error_code)) {
+ return;
+ }
+
+ SPOOFCHECKER_METHOD_FETCH_OBJECT;
+
+ ret = uspoof_areConfusableUTF8(co->uspoof, s1, s1_len, s2, s2_len, SPOOFCHECKER_ERROR_CODE_P(co));
+
+ if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
+ RETURN_TRUE;
+ }
+
+ if (error_code) {
+ zval_dtor(error_code);
+ ZVAL_LONG(error_code, ret);
+ }
+ RETVAL_BOOL(ret != 0);
+}
+/* }}} */
+
+/* {{{ proto void Spoofchecker::setAllowedLocales( string locales )
+ * Locales to use when running checks
+ */
+PHP_METHOD(Spoofchecker, setAllowedLocales)
+{
+ char *locales;
+ int locales_len;
+ SPOOFCHECKER_METHOD_INIT_VARS;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &locales, &locales_len)) {
+ return;
+ }
+
+ SPOOFCHECKER_METHOD_FETCH_OBJECT;
+
+ uspoof_setAllowedLocales(co->uspoof, locales, SPOOFCHECKER_ERROR_CODE_P(co));
+
+ if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
+ return;
+ }
+}
+/* }}} */
+
+/* {{{ proto void Spoofchecker::setChecks( int checks )
+ * Set the checks to run
+ */
+PHP_METHOD(Spoofchecker, setChecks)
+{
+ long checks;
+ SPOOFCHECKER_METHOD_INIT_VARS;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &checks)) {
+ return;
+ }
+
+ SPOOFCHECKER_METHOD_FETCH_OBJECT;
+
+ uspoof_setChecks(co->uspoof, checks, SPOOFCHECKER_ERROR_CODE_P(co));
+
+ if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
+ }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/spoofchecker/spoofchecker_main.h b/ext/intl/spoofchecker/spoofchecker_main.h
new file mode 100644
index 0000000..fb920d7
--- /dev/null
+++ b/ext/intl/spoofchecker/spoofchecker_main.h
@@ -0,0 +1,27 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Scott MacVicar <scottmac@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef SPOOFCHECKER_MAIN_H
+#define SPOOFCHECKER_MAIN_H
+
+#include <php.h>
+
+PHP_METHOD(Spoofchecker, isSuspicious);
+PHP_METHOD(Spoofchecker, areConfusable);
+PHP_METHOD(Spoofchecker, setAllowedLocales);
+PHP_METHOD(Spoofchecker, setChecks);
+
+#endif // SPOOFCHECKER_MAIN_H
diff --git a/ext/intl/tests/_files/es-bundle.txt b/ext/intl/tests/_files/es-bundle.txt
new file mode 100644
index 0000000..46399a7
--- /dev/null
+++ b/ext/intl/tests/_files/es-bundle.txt
@@ -0,0 +1,21 @@
+es {
+ teststring:string { "Hola Mundo!" }
+
+ testint:int { 2 }
+
+ testvector:intvector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }
+
+ testbin:bin { a1b2c3d4e5f67890 }
+
+ testtable:table {
+ major:int { 3 }
+ minor:int { 4 }
+ patch:int { 7 }
+ }
+
+ testarray:array {
+ "cadena 1",
+ "cadena 2",
+ "cadena 3"
+ }
+}
diff --git a/ext/intl/tests/_files/res_index.txt b/ext/intl/tests/_files/res_index.txt
new file mode 100644
index 0000000..a39bea5
--- /dev/null
+++ b/ext/intl/tests/_files/res_index.txt
@@ -0,0 +1,6 @@
+res_index:table(nofallback) {
+ InstalledLocales {
+es {""}
+root {""}
+ }
+} \ No newline at end of file
diff --git a/ext/intl/tests/_files/resourcebundle.txt b/ext/intl/tests/_files/resourcebundle.txt
new file mode 100644
index 0000000..5b081da
--- /dev/null
+++ b/ext/intl/tests/_files/resourcebundle.txt
@@ -0,0 +1,21 @@
+root {
+ teststring:string { "Hello World!" }
+
+ testint:int { 2 }
+
+ testvector:intvector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }
+
+ testbin:bin { a1b2c3d4e5f67890 }
+
+ testtable:table {
+ major:int { 3 }
+ minor:int { 4 }
+ patch:int { 7 }
+ }
+
+ testarray:array {
+ "string 1",
+ "string 2",
+ "string 3"
+ }
+}
diff --git a/ext/intl/tests/_files/resourcebundle/es.res b/ext/intl/tests/_files/resourcebundle/es.res
new file mode 100644
index 0000000..f9d891d
--- /dev/null
+++ b/ext/intl/tests/_files/resourcebundle/es.res
Binary files differ
diff --git a/ext/intl/tests/_files/resourcebundle/res_index.res b/ext/intl/tests/_files/resourcebundle/res_index.res
new file mode 100644
index 0000000..9dd3df2
--- /dev/null
+++ b/ext/intl/tests/_files/resourcebundle/res_index.res
Binary files differ
diff --git a/ext/intl/tests/_files/resourcebundle/root.res b/ext/intl/tests/_files/resourcebundle/root.res
new file mode 100644
index 0000000..62cb48c
--- /dev/null
+++ b/ext/intl/tests/_files/resourcebundle/root.res
Binary files differ
diff --git a/ext/intl/tests/badargs.phpt b/ext/intl/tests/badargs.phpt
new file mode 100644
index 0000000..9232bbf
--- /dev/null
+++ b/ext/intl/tests/badargs.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Check that bad argumens return the same
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+$funcs = get_extension_funcs("intl");
+function ignore_err() {}
+set_error_handler("ignore_err");
+$arg = new stdClass();
+foreach($funcs as $func) {
+ $rfunc = new ReflectionFunction($func);
+ if($rfunc->getNumberOfRequiredParameters() == 0) {
+ continue;
+ }
+ $res = $func($arg);
+ if($res != false) {
+ echo "$func: ";
+ var_dump($res);
+ }
+}
+echo "OK!\n";
+?>
+--EXPECT--
+OK!
diff --git a/ext/intl/tests/bug12887.phpt b/ext/intl/tests/bug12887.phpt
new file mode 100644
index 0000000..e2fc194
--- /dev/null
+++ b/ext/intl/tests/bug12887.phpt
@@ -0,0 +1,29 @@
+--TEST--
+locale_get_keywords() bug #12887
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function ut_main()
+{
+ $res_str = '';
+ $keywords_arr = ut_loc_get_keywords( 'de_DE@currency=EUR;collation=PHONEBOOK;sort=PHONEBOOK' );
+ if ($keywords_arr) {
+ foreach( $keywords_arr as $key => $value){
+ $res_str .= "$key = $value\n";
+ }
+ }
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+collation = PHONEBOOK
+currency = EUR
+sort = PHONEBOOK
diff --git a/ext/intl/tests/bug14562.phpt b/ext/intl/tests/bug14562.phpt
new file mode 100644
index 0000000..3256268
--- /dev/null
+++ b/ext/intl/tests/bug14562.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Bug #14562 NumberFormatter breaks when locale changes
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function ut_main()
+{
+ $res_str = "";
+ $de_locale="de_DE.UTF-8";
+ $fmt = new NumberFormatter("de", NumberFormatter::DECIMAL );
+ $numeric = $fmt->parse("1234,56");
+ $res_str .= "$numeric\n";
+ setlocale(LC_ALL, $de_locale);
+ $fmt = new NumberFormatter("de", NumberFormatter::DECIMAL );
+ $numeric = $fmt->parse("1234,56");
+ $res_str .= "$numeric\n";
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+1234.56
+1234.56
diff --git a/ext/intl/tests/bug48227.phpt b/ext/intl/tests/bug48227.phpt
new file mode 100644
index 0000000..0ac0d5e
--- /dev/null
+++ b/ext/intl/tests/bug48227.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #48227 (NumberFormatter::format leaks memory)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+$x = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
+var_dump($x->format(''));
+var_dump($x->format(1));
+var_dump($x->format(NULL));
+var_dump($x->format($x));
+
+?>
+--EXPECTF--
+string(1) "0"
+string(1) "1"
+string(1) "0"
+
+Notice: Object of class NumberFormatter could not be converted to int in %s on line %d
+string(1) "1"
diff --git a/ext/intl/tests/bug50590.phpt b/ext/intl/tests/bug50590.phpt
new file mode 100644
index 0000000..c39c333
--- /dev/null
+++ b/ext/intl/tests/bug50590.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #50590 (IntlDateFormatter::parse result is limited to the integer range)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+$fmt = new IntlDateFormatter("en_US", IntlDateFormatter::FULL, IntlDateFormatter::FULL);
+var_dump($fmt->parse("Wednesday, January 20, 2038 3:14:07 AM GMT"));
+
+?>
+--EXPECTF--
+%s(2147570047)
diff --git a/ext/intl/tests/bug53512.phpt b/ext/intl/tests/bug53512.phpt
new file mode 100644
index 0000000..a1b1dcf
--- /dev/null
+++ b/ext/intl/tests/bug53512.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Bug #53512 (NumberFormatter::setSymbol crash on bogus $attr values)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+$badvals = array(4294901761, 2147483648, -2147483648, -1);
+
+foreach ($badvals as $val) {
+ $x = numfmt_create("en", NumberFormatter::PATTERN_DECIMAL);
+ var_dump(numfmt_set_symbol($x, $val, ""));
+ var_dump(intl_get_error_message());
+}
+
+?>
+--EXPECTF--
+bool(false)
+string(65) "numfmt_set_symbol: invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(65) "numfmt_set_symbol: invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(65) "numfmt_set_symbol: invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
+bool(false)
+string(65) "numfmt_set_symbol: invalid symbol value: U_ILLEGAL_ARGUMENT_ERROR"
diff --git a/ext/intl/tests/bug55562.phpt b/ext/intl/tests/bug55562.phpt
new file mode 100644
index 0000000..dd053c0
--- /dev/null
+++ b/ext/intl/tests/bug55562.phpt
@@ -0,0 +1,13 @@
+--TEST--
+grapheme_substr() - Bug55562 - grapheme_substr() returns false if length parameter is to large
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+var_dump(
+ grapheme_substr('FOK', 1, 20), // expected: OK
+ grapheme_substr('한국어', 1, 20) //expected: 국어
+);
+--EXPECT--
+string(2) "OK"
+string(6) "국어"
diff --git a/ext/intl/tests/bug59597_32.phpt b/ext/intl/tests/bug59597_32.phpt
new file mode 100644
index 0000000..928a235
--- /dev/null
+++ b/ext/intl/tests/bug59597_32.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug#59597 NumberFormatter::parse() with TYPE_INT64 results in a 32 bit integer
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php echo PHP_INT_SIZE == 8 ? "skip 32-bit only" : "OK"; ?>
+--FILE--
+<?php
+
+$formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
+$value = $formatter->parse('2147483647', \NumberFormatter::TYPE_INT32);
+var_dump($value);
+
+$formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
+$value = $formatter->parse('2147483650', \NumberFormatter::TYPE_INT64);
+var_dump($value);
+
+?>
+--EXPECT--
+int(2147483647)
+float(2147483650)
+
diff --git a/ext/intl/tests/bug59597_64.phpt b/ext/intl/tests/bug59597_64.phpt
new file mode 100644
index 0000000..eb70995
--- /dev/null
+++ b/ext/intl/tests/bug59597_64.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug#59597 NumberFormatter::parse() with TYPE_INT64 results in a 32 bit integer
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php echo PHP_INT_SIZE != 8 ? "skip 64-bit only" : "OK"; ?>
+--FILE--
+<?php
+
+$formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
+$value = $formatter->parse('2147483647', \NumberFormatter::TYPE_INT32);
+var_dump($value);
+
+$formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL);
+$value = $formatter->parse('2147483650', \NumberFormatter::TYPE_INT64);
+var_dump($value);
+
+?>
+--EXPECT--
+int(2147483647)
+int(2147483650)
diff --git a/ext/intl/tests/bug60192-compare.phpt b/ext/intl/tests/bug60192-compare.phpt
new file mode 100644
index 0000000..0fd24cc
--- /dev/null
+++ b/ext/intl/tests/bug60192-compare.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Bug #60192 (SegFault when Collator not constructed properly)
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl')) { die('skip intl extension not available'); }
+?>
+--FILE--
+<?php
+class Collator2 extends Collator{
+ public function __construct() {
+ // ommitting parent::__construct($someLocale);
+ }
+}
+
+$c = new Collator2();
+$a = $c->compare('h', 'H');
+--EXPECTF--
+
+Catchable fatal error: Collator::compare(): Object not initialized in %s on line %d
diff --git a/ext/intl/tests/bug60192-getlocale.phpt b/ext/intl/tests/bug60192-getlocale.phpt
new file mode 100644
index 0000000..50e3ec3
--- /dev/null
+++ b/ext/intl/tests/bug60192-getlocale.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #60192 (SegFault when Collator not constructed properly)
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl')) { die('skip intl extension not available'); }
+?>
+--FILE--
+<?php
+
+class Collator2 extends Collator{
+ public function __construct() {
+ // ommitting parent::__construct($someLocale);
+ }
+}
+
+$c = new Collator2();
+$c->getLocale(Locale::ACTUAL_LOCALE);
+--EXPECTF--
+
+Catchable fatal error: Collator::getLocale(): Object not initialized in %s on line %d
diff --git a/ext/intl/tests/bug60192-getsortkey.phpt b/ext/intl/tests/bug60192-getsortkey.phpt
new file mode 100644
index 0000000..39755ae
--- /dev/null
+++ b/ext/intl/tests/bug60192-getsortkey.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #60192 (SegFault when Collator not constructed properly)
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl')) { die('skip intl extension not available'); }
+?>
+--FILE--
+<?php
+
+class Collator2 extends Collator{
+ public function __construct() {
+ // ommitting parent::__construct($someLocale);
+ }
+}
+
+$c = new Collator2();
+$c->getSortKey('h');
+--EXPECTF--
+
+Catchable fatal error: Collator::getSortKey(): Object not initialized in %s on line %d
diff --git a/ext/intl/tests/bug60192-sort.phpt b/ext/intl/tests/bug60192-sort.phpt
new file mode 100644
index 0000000..5705721
--- /dev/null
+++ b/ext/intl/tests/bug60192-sort.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #60192 (SegFault when Collator not constructed properly)
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl')) { die('skip intl extension not available'); }
+?>
+--FILE--
+<?php
+
+class Collator2 extends Collator{
+ public function __construct() {
+ // ommitting parent::__construct($someLocale);
+ }
+}
+
+$c = new Collator2();
+$a = array('a', 'b');
+$c->sort($a);
+--EXPECTF--
+
+Catchable fatal error: Collator::sort(): Object not initialized in %s on line %d
diff --git a/ext/intl/tests/bug60192-sortwithsortkeys.phpt b/ext/intl/tests/bug60192-sortwithsortkeys.phpt
new file mode 100644
index 0000000..445f4a0
--- /dev/null
+++ b/ext/intl/tests/bug60192-sortwithsortkeys.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #60192 (SegFault when Collator not constructed properly)
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl')) { die('skip intl extension not available'); }
+?>
+--FILE--
+<?php
+
+class Collator2 extends Collator{
+ public function __construct() {
+ // ommitting parent::__construct($someLocale);
+ }
+}
+
+$c = new Collator2();
+$a = array('a', 'b');
+$c->sortWithSortKeys($a);
+--EXPECTF--
+
+Catchable fatal error: Collator::sortWithSortKeys(): Object not initialized in %s on line %d
diff --git a/ext/intl/tests/bug61487.phpt b/ext/intl/tests/bug61487.phpt
new file mode 100644
index 0000000..361debe
--- /dev/null
+++ b/ext/intl/tests/bug61487.phpt
@@ -0,0 +1,11 @@
+--TEST--
+grapheme() str[i]pos limits
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+var_dump(grapheme_stripos(1,1,2147483648));
+var_dump(grapheme_strpos(1,1,2147483648));
+--EXPECT--
+bool(false)
+bool(false)
diff --git a/ext/intl/tests/bug62017.phpt b/ext/intl/tests/bug62017.phpt
new file mode 100644
index 0000000..13c4fe5
--- /dev/null
+++ b/ext/intl/tests/bug62017.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #62017: datefmt_create with incorrectly encoded timezone leaks pattern
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+ini_set('intl.error_level', E_WARNING);
+var_dump(
+ datefmt_create('', IntlDateFormatter::NONE, IntlDateFormatter::NONE, "\xFF",
+ IntlDateFormatter::GREGORIAN, 'a'));
+var_dump(
+ new IntlDateFormatter('', IntlDateFormatter::NONE, IntlDateFormatter::NONE, "Europe/Lisbon",
+ IntlDateFormatter::GREGORIAN, "\x80"));
+--EXPECTF--
+Warning: datefmt_create(): datefmt_create: error converting timezone_str to UTF-16 in %s on line %d
+NULL
+
+Warning: IntlDateFormatter::__construct(): datefmt_create: error converting pattern to UTF-16 in %s on line %d
+NULL
+
diff --git a/ext/intl/tests/bug62070.phpt b/ext/intl/tests/bug62070.phpt
new file mode 100644
index 0000000..3ab0078
--- /dev/null
+++ b/ext/intl/tests/bug62070.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #62070: Collator::getSortKey() returns garbage
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '49') < 0)
+ die('skip ICU >= 49 only');
+--FILE--
+<?php
+$s1 = 'Hello';
+
+$coll = collator_create('en_US');
+$res = collator_get_sort_key($coll, $s1);
+
+echo urlencode($res);
+--EXPECT--
+5%2F%3D%3DC%01%09%01%8F%08
diff --git a/ext/intl/tests/bug62081.phpt b/ext/intl/tests/bug62081.phpt
new file mode 100644
index 0000000..7d9e2ce
--- /dev/null
+++ b/ext/intl/tests/bug62081.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Bug #62081: IntlDateFormatter leaks memory if called twice
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+ini_set('intl.error_level', E_WARNING);
+$x = new IntlDateFormatter(1,1,1,1,1);
+var_dump($x->__construct(1,1,1,1,1));
+--EXPECTF--
+Warning: IntlDateFormatter::__construct(): datefmt_create: cannot call constructor twice in %s on line %d
+NULL
diff --git a/ext/intl/tests/bug62082.phpt b/ext/intl/tests/bug62082.phpt
new file mode 100644
index 0000000..e6ca73e
--- /dev/null
+++ b/ext/intl/tests/bug62082.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Bug #62082: Memory corruption in internal get_icu_disp_value_src_php()
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+var_dump(locale_get_display_name(str_repeat("a", 300), null));
+var_dump(locale_get_display_name(str_repeat("a", 512), null));
+var_dump(locale_get_display_name(str_repeat("a", 600), null));
+--EXPECT--
+string(300) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+string(512) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+string(600) "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
diff --git a/ext/intl/tests/bug62083.phpt b/ext/intl/tests/bug62083.phpt
new file mode 100644
index 0000000..4baa5c5
--- /dev/null
+++ b/ext/intl/tests/bug62083.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Bug #62083: grapheme_extract() leaks memory
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+$arr1 = array();
+var_dump(grapheme_extract(-1, -1, -1,-1, $arr1));
+--EXPECT--
+bool(false)
diff --git a/ext/intl/tests/bug62915-2.phpt b/ext/intl/tests/bug62915-2.phpt
new file mode 100644
index 0000000..bcb069c
--- /dev/null
+++ b/ext/intl/tests/bug62915-2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Bug #62915: cloning of several classes is defective
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+class A extends IntlDateFormatter {
+ static $ARGS = array("en_US" ,IntlDateFormatter::FULL, IntlDateFormatter::FULL,
+ 'America/Los_Angeles', IntlDateFormatter::GREGORIAN);
+}
+class B extends NumberFormatter {
+ static $ARGS = array('de_DE', NumberFormatter::DECIMAL);
+}
+class C extends MessageFormatter {
+ static $ARGS = array("en_US", "foo");
+}
+class D extends Spoofchecker {
+ static $ARGS = array();
+}
+
+foreach (range('A', 'D') as $subclass) {
+ $rc = new ReflectionClass($subclass);
+ $obj = $rc->newInstanceArgs($subclass::$ARGS);
+ $clone = clone $obj;
+ var_dump(get_class($clone));
+}
+--EXPECT--
+string(1) "A"
+string(1) "B"
+string(1) "C"
+string(1) "D"
diff --git a/ext/intl/tests/collation_customization.phpt b/ext/intl/tests/collation_customization.phpt
new file mode 100644
index 0000000..e380380
--- /dev/null
+++ b/ext/intl/tests/collation_customization.phpt
@@ -0,0 +1,70 @@
+--TEST--
+Collation customization
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Check effects of changing misc collattion options.
+ */
+
+
+function cmp_array( &$coll, $a )
+{
+ $res = '';
+ $prev = null;
+ foreach( $a as $i )
+ {
+ if( is_null( $prev ) )
+ $res .= "$i";
+ else
+ {
+ $eqrc = ut_coll_compare( $coll, $prev, $i );
+ $eq = $eqrc < 0 ? "<" : ( $eqrc > 0 ? ">" : "=" );
+ $res .= " $eq $i";
+ }
+
+ $prev = $i;
+ }
+ $res .= "\n";
+
+ return $res;
+}
+
+function check_alternate_handling( &$coll )
+{
+ $res = '';
+
+ ut_coll_set_strength( $coll, Collator::TERTIARY );
+ ut_coll_set_attribute( $coll, Collator::ALTERNATE_HANDLING, Collator::NON_IGNORABLE );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'Di Silva', 'diSilva', 'U.S.A.', 'USA' ) );
+
+ ut_coll_set_attribute( $coll, Collator::ALTERNATE_HANDLING, Collator::SHIFTED );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'diSilva', 'Di Silva', 'U.S.A.', 'USA' ) );
+
+ ut_coll_set_strength( $coll, Collator::QUATERNARY );
+
+ $res .= cmp_array( $coll, array( 'di Silva', 'diSilva', 'Di Silva', 'U.S.A.', 'USA' ) );
+ $res .= "\n";
+
+ return $res;
+}
+
+function ut_main()
+{
+ $coll = ut_coll_create( 'en_US' );
+
+ return
+ check_alternate_handling( $coll );
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+di Silva < Di Silva < diSilva < U.S.A. < USA
+di Silva = diSilva < Di Silva < U.S.A. = USA
+di Silva < diSilva < Di Silva < U.S.A. < USA
diff --git a/ext/intl/tests/collator_asort.phpt b/ext/intl/tests/collator_asort.phpt
new file mode 100644
index 0000000..a614ddc
--- /dev/null
+++ b/ext/intl/tests/collator_asort.phpt
@@ -0,0 +1,242 @@
+--TEST--
+asort()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort associative arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort various arrays in specified locale.
+ */
+function sort_arrays( $locale, $test_arrays, $sort_flag = Collator::SORT_REGULAR )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $test_arrays as $test_array )
+ {
+ // Try to sort test data.
+ $res_val = ut_coll_asort( $coll, $test_array, $sort_flag );
+
+ // Return output data.
+ $res_dump = "\n" . dump( $test_array ) .
+ "\n Result: " . dump( $res_val );
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+/*
+ * Test main function.
+ */
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'd' => 'y' ,
+ 'c' => 'i' ,
+ 'a' => 'k' ),
+
+ array( 'a' => 'a' ,
+ 'b' => 'aaa',
+ 'c' => 'aa' ),
+
+ array( 'a' => 'a' ,
+ 'aaa'=> 'a' ,
+ 'aa' => 'a' ),
+
+ array( '1' => 'abc',
+ '5' => '!' ,
+ '2' => null ,
+ '7' => '' ),
+
+ array( '1' => '100',
+ '2' => '25' ,
+ '3' => '36' ),
+
+ array( '1' => 5 ,
+ '2' => '30' ,
+ '3' => 2 )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ // Sort an array in SORT_STRING mode using en_US locale.
+ $test_params = array(
+ array( '1' => '100',
+ '2' => '25' ,
+ '3' => '36' ),
+
+ array( '1' => 5 ,
+ '2' => '30' ,
+ '3' => 2 ),
+
+ array( '1' => 'd' ,
+ '2' => '' ,
+ '3' => ' a' ),
+
+ array( '1' => 'y' ,
+ '2' => 'k' ,
+ '3' => 'i' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params, Collator::SORT_STRING );
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'п' => 'у',
+ 'б' => 'в',
+ 'е' => 'а' ),
+
+ array( '1' => 'п',
+ '4' => '',
+ '7' => 'd',
+ '2' => 'пп' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+
+ // Sort an array using Lithuanian locale.
+ $test_params = array(
+ array( 'd' => 'y',
+ 'c' => 'i',
+ 'a' => 'k' )
+ );
+
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str . "\n";
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.162b81ac12878b817fc39063097e45b5:
+array (
+ 'c' => 'i',
+ 'a' => 'k',
+ 'd' => 'y',
+)
+ Result: true
+
+Test 2.93d96e22f692d8a281b0a389f01f8d1e:
+array (
+ 'a' => 'a',
+ 'c' => 'aa',
+ 'b' => 'aaa',
+)
+ Result: true
+
+Test 3.9f25de4482bc7b58de508e278113317c:
+array (
+ 'aa' => 'a',
+ 'aaa' => 'a',
+ 'a' => 'a',
+)
+ Result: true
+
+Test 4.a85a41ea78e45b651080cfd98c0b431d:
+array (
+ 7 => '',
+ 2 => NULL,
+ 5 => '!',
+ 1 => 'abc',
+)
+ Result: true
+
+Test 5.99dc71f405b286e03d489061b36e6900:
+array (
+ 2 => '25',
+ 3 => '36',
+ 1 => '100',
+)
+ Result: true
+
+Test 6.bf5bba243307c9d12934e756ad4be190:
+array (
+ 3 => 2,
+ 1 => 5,
+ 2 => '30',
+)
+ Result: true
+
+Test 7.e4ee7024c61476e9e7a6c28b5e47df6f:
+array (
+ 1 => '100',
+ 2 => '25',
+ 3 => '36',
+)
+ Result: true
+
+Test 8.5fa7033dd43784be0db1474eb48b83c8:
+array (
+ 3 => 2,
+ 2 => '30',
+ 1 => 5,
+)
+ Result: true
+
+Test 9.588cdf4692bc09aa92ffe7e48f9e4579:
+array (
+ 2 => '',
+ 3 => ' a',
+ 1 => 'd',
+)
+ Result: true
+
+Test 10.be02641a47ebcccd23e4183ca3a415f7:
+array (
+ 3 => 'i',
+ 2 => 'k',
+ 1 => 'y',
+)
+ Result: true
+
+Test 11.153d9b11d1e5936afc917a94a4e11f34:
+array (
+ 'е' => 'а',
+ 'б' => 'в',
+ 'п' => 'у',
+)
+ Result: true
+
+Test 12.6eaea3fa21b3b7d006ca7dfa27d4e282:
+array (
+ 4 => '',
+ 7 => 'd',
+ 1 => 'п',
+ 2 => 'пп',
+)
+ Result: true
+
+Test 13.8800d48abb960a59002eef77f1d73ae0:
+array (
+ 'c' => 'i',
+ 'd' => 'y',
+ 'a' => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/collator_compare.phpt b/ext/intl/tests/collator_compare.phpt
new file mode 100644
index 0000000..f10b570
--- /dev/null
+++ b/ext/intl/tests/collator_compare.phpt
@@ -0,0 +1,134 @@
+--TEST--
+compare()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Compare various string pairs using various locales.
+ */
+
+
+/*
+ * Converts comparison result to a character.
+ */
+function cmp_to_char( $comp_res )
+{
+ switch( $comp_res )
+ {
+ case 0: // UCOL_EQUAL
+ return '=';
+ case 1: // UCOL_GREATER
+ return '>';
+ case -1: // UCOL_LESS
+ return '<';
+ default:
+ return '?';
+ }
+}
+
+/*
+ * Compare string pairs in the given array
+ * using specified locale.
+ */
+function compare_pairs( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $test_array as $test_strings )
+ {
+ list( $str1, $str2 ) = $test_strings;
+
+ // Compare strings.
+ $res_val = cmp_to_char( ut_coll_compare( $coll, $str1, $str2 ) );
+
+ // Concatenate result strings.
+ $res_str .= dump( $str1 ) .
+ ' ' . $res_val . ' ' .
+ dump( $str2 ) . "\n";
+ }
+
+ return $res_str;
+
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Compare strings using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abc' ),
+ array( 'Abc', 'abc' ),
+ array( 'a' , 'abc' ),
+ array( 'a' , '' ),
+ array( '' , '' ),
+ array( 'a' , 'b' ),
+ array( 'ab' , 'b' ),
+ array( 'ab' , 'a' ),
+ array( 123 , 'abc' ),
+ array( 'ac' , null ),
+ array( '.' , '.' ),
+ // Try to compare long strings.
+ array( 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcde',
+ 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdea'),
+ array( null , null )
+ );
+
+ $res_str .= compare_pairs( 'en_US', $test_params );
+
+
+ // Compare strings using ru_RU locale.
+ $test_params = array(
+ array( 'а', 'б' ),
+ array( 'а', 'аа' ),
+ array( 'аб', 'ба' ),
+ array( 'а', ',' ),
+ array( 'а', 'b' ),
+ array( 'а', 'bb' ),
+ array( 'а', 'ab' ),
+ array( 'а', null )
+ );
+
+ $res_str .= compare_pairs( 'ru_RU', $test_params );
+
+
+ // Compare strings using lt_LT locale.
+ $test_params = array(
+ array( 'y', 'k' )
+ );
+
+ $res_str .= compare_pairs( 'lt_LT', $test_params );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+'abc' = 'abc'
+'Abc' > 'abc'
+'a' < 'abc'
+'a' > ''
+'' = ''
+'a' < 'b'
+'ab' < 'b'
+'ab' > 'a'
+123 < 'abc'
+'ac' > NULL
+'.' = '.'
+'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcde' < 'abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdea'
+NULL = NULL
+'а' < 'б'
+'а' < 'аа'
+'аб' < 'ба'
+'а' > ','
+'а' > 'b'
+'а' > 'bb'
+'а' > 'ab'
+'а' > NULL
+'y' < 'k'
diff --git a/ext/intl/tests/collator_create.phpt b/ext/intl/tests/collator_create.phpt
new file mode 100644
index 0000000..eed8cf1
--- /dev/null
+++ b/ext/intl/tests/collator_create.phpt
@@ -0,0 +1,82 @@
+--TEST--
+create() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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 80 characters: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/collator_create2.phpt b/ext/intl/tests/collator_create2.phpt
new file mode 100644
index 0000000..90d71b0
--- /dev/null
+++ b/ext/intl/tests/collator_create2.phpt
@@ -0,0 +1,82 @@
+--TEST--
+create() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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 = 'root'
+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 80 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 100644
index 0000000..45a8e71
--- /dev/null
+++ b/ext/intl/tests/collator_get_error_code.phpt
@@ -0,0 +1,47 @@
+--TEST--
+get_error_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Retreive error code.
+ */
+
+
+/*
+ * Check if error code equals to expected one.
+ */
+function check_rc( $rc, $expected )
+{
+ return ( $rc === $expected ? "ok" : "failed" ) . "\n";
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'ru_RU' );
+
+ // Try specifying a correct attribute.
+ ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ $status = ut_coll_get_error_code( $coll );
+ $res .= check_rc( $status, U_ZERO_ERROR );
+
+ // Try specifying an incorrect attribute.
+ ut_coll_get_attribute( $coll, 12345 );
+ $status = ut_coll_get_error_code( $coll );
+ $res .= check_rc( $status, U_ILLEGAL_ARGUMENT_ERROR );
+
+ return $res;
+}
+
+# Suppress warning messages.
+error_reporting( E_ERROR );
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+ok
+ok
diff --git a/ext/intl/tests/collator_get_error_message.phpt b/ext/intl/tests/collator_get_error_message.phpt
new file mode 100644
index 0000000..af4b9c3
--- /dev/null
+++ b/ext/intl/tests/collator_get_error_message.phpt
@@ -0,0 +1,36 @@
+--TEST--
+get_error_message()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Retreive error message.
+ */
+
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'ru_RU' );
+
+ // Try specifying a correct attribute.
+ ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ $status = ut_coll_get_error_message( $coll );
+ $res .= $status . "\n";
+
+ // Try specifying an incorrect attribute.
+ ut_coll_get_attribute( $coll, 12345 );
+ $status = ut_coll_get_error_message( $coll );
+ $res .= $status . "\n";
+
+ return $res;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+U_ZERO_ERROR
+Error getting attribute value: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/collator_get_locale.phpt b/ext/intl/tests/collator_get_locale.phpt
new file mode 100644
index 0000000..68440f3
--- /dev/null
+++ b/ext/intl/tests/collator_get_locale.phpt
@@ -0,0 +1,51 @@
+--TEST--
+get_locale() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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,
+ );
+
+ $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'
diff --git a/ext/intl/tests/collator_get_locale2.phpt b/ext/intl/tests/collator_get_locale2.phpt
new file mode 100644
index 0000000..798c792
--- /dev/null
+++ b/ext/intl/tests/collator_get_locale2.phpt
@@ -0,0 +1,51 @@
+--TEST--
+get_locale() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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,
+ );
+
+ $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 'root'
+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'
diff --git a/ext/intl/tests/collator_get_set_attribute.phpt b/ext/intl/tests/collator_get_set_attribute.phpt
new file mode 100644
index 0000000..b234790
--- /dev/null
+++ b/ext/intl/tests/collator_get_set_attribute.phpt
@@ -0,0 +1,41 @@
+--TEST--
+get/set_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try to set/get a collation attribute.
+ */
+
+
+/*
+ * Return current normalication mode.
+ */
+function check_val( $coll )
+{
+ $val = ut_coll_get_attribute( $coll, Collator::NORMALIZATION_MODE );
+ return sprintf( "%s\n", ( $val == Collator::OFF ? "off" : "on" ) );
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'en_US' );
+
+ ut_coll_set_attribute( $coll, Collator::NORMALIZATION_MODE, Collator::OFF );
+ $res .= check_val( $coll );
+
+ ut_coll_set_attribute( $coll, Collator::NORMALIZATION_MODE, Collator::ON );
+ $res .= check_val( $coll );
+
+ return $res;
+}
+
+include( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+off
+on
diff --git a/ext/intl/tests/collator_get_set_strength.phpt b/ext/intl/tests/collator_get_set_strength.phpt
new file mode 100644
index 0000000..1f77944
--- /dev/null
+++ b/ext/intl/tests/collator_get_set_strength.phpt
@@ -0,0 +1,41 @@
+--TEST--
+get/set_strength()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try to set/get collation strength.
+ */
+
+/*
+ * Set given collation strength, then get it back
+ * and check if it's the same.
+ */
+function check_set_strength( $coll, $val )
+{
+ ut_coll_set_strength( $coll, $val );
+ $new_val = ut_coll_get_strength( $coll );
+ return ( $new_val == $val ? "ok" : "failed" ) . "\n";
+}
+
+function ut_main()
+{
+ $res = '';
+ $coll = ut_coll_create( 'en_US' );
+
+ $res .= check_set_strength( $coll, Collator::PRIMARY );
+ $res .= check_set_strength( $coll, Collator::SECONDARY );
+ $res .= check_set_strength( $coll, Collator::TERTIARY );
+
+ return $res;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+ok
+ok
+ok
diff --git a/ext/intl/tests/collator_get_sort_key.phpt b/ext/intl/tests/collator_get_sort_key.phpt
new file mode 100644
index 0000000..a9c4d71
--- /dev/null
+++ b/ext/intl/tests/collator_get_sort_key.phpt
@@ -0,0 +1,98 @@
+--TEST--
+collator_get_sort_key()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip ICU >= 4.8 only'; ?>
+--FILE--
+<?php
+
+/*
+ * Get sort keys using various locales
+ */
+function sort_arrays( $locale, $data )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach($data as $value) {
+ $res_val = ut_coll_get_sort_key( $coll, $value );
+ $res_str .= "source: ".$value."\n".
+ "key: ".bin2hex($res_val)."\n";
+ }
+
+ return $res_str;
+}
+
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Regular strings keys
+ $test_params = array(
+ 'abc', 'abd', 'aaa',
+ 'аа', 'а', 'z',
+ '', null , '3',
+ 'y' , 'i' , 'k'
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ 'абг', 'абв', 'жжж', 'эюя'
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+ // 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--
+source: abc
+key: 27292b01070107
+source: abd
+key: 27292d01070107
+source: aaa
+key: 27272701070107
+source: аа
+key: 5c0a0a01060106
+source: а
+key: 5c0a01050105
+source: z
+key: 5901050105
+source:
+key: 0101
+source:
+key: 0101
+source: 3
+key: 1801050105
+source: y
+key: 5701050105
+source: i
+key: 3701050105
+source: k
+key: 3b01050105
+source: абг
+key: 5c0a161a01070107
+source: абв
+key: 5c0a161801070107
+source: жжж
+key: 5c3a3a3a01070107
+source: эюя
+key: 5d3b3f4501070107
+source: абг
+key: 5c0a161a01070107
+source: абв
+key: 5c0a161801070107
+source: жжж
+key: 5c3a3a3a01070107
+source: эюя
+key: 5d3b3f4501070107 \ No newline at end of file
diff --git a/ext/intl/tests/collator_sort.phpt b/ext/intl/tests/collator_sort.phpt
new file mode 100644
index 0000000..5cefe2f
--- /dev/null
+++ b/ext/intl/tests/collator_sort.phpt
@@ -0,0 +1,247 @@
+--TEST--
+sort()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort arrays in the given list using specified locale.
+ */
+function sort_arrays( $locale, $arrays, $sort_flag = Collator::SORT_REGULAR )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $arrays as $array )
+ {
+ // Sort array values
+ $res_val = ut_coll_sort( $coll, $array, $sort_flag );
+
+ // Concatenate the sorted array and function result
+ // with output string.
+ $res_dump = "\n" . dump( $array ) .
+ "\n Result: " . dump( $res_val );
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abd', 'aaa' ),
+ array( 'm' , '1' , '_' ),
+ array( 'a' , 'aaa', 'aa' ),
+ array( 'ba' , 'b' , 'ab' ),
+ array( 'e' , 'c' , 'a' ),
+ array( '100', '25' , '36' ),
+ array( 5 , '30' , 2 ),
+ array( 'd' , '' , ' a' ),
+ array( 'd ' , 'f ' , ' a' ),
+ array( 'a' , null , '3' ),
+ array( 'y' , 'k' , 'i' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ $test_params = array(
+ array( '100', '25' , '36' ),
+ array( 5 , '30' , 2 ),
+ array( 'd' , '' , ' a' ),
+ array( 'y' , 'k' , 'i' )
+ );
+
+ // Sort in en_US locale with SORT_STRING flag
+ $res_str .= sort_arrays( 'en_US', $test_params, Collator::SORT_STRING );
+
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'абг', 'абв', 'ааа', 'abc' ),
+ array( 'аа', 'ааа' , 'а' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+ // Sort an array using Lithuanian locale.
+ $test_params = array(
+ array( 'y' , 'k' , 'i' )
+ );
+
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.e8f1cd28133d79ecd660002f1c660d0e:
+array (
+ 0 => 'aaa',
+ 1 => 'abc',
+ 2 => 'abd',
+)
+ Result: true
+
+Test 2.c2ded12173dd2996927378cae37eb275:
+array (
+ 0 => '_',
+ 1 => '1',
+ 2 => 'm',
+)
+ Result: true
+
+Test 3.54071c968d71cb98c5d379145f8d7d38:
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+ Result: true
+
+Test 4.19abe63d6f6dfef65b0e3c9ab4826b07:
+array (
+ 0 => 'ab',
+ 1 => 'b',
+ 2 => 'ba',
+)
+ Result: true
+
+Test 5.9a8dc0a9bc771368c2f1fc3d02754610:
+array (
+ 0 => 'a',
+ 1 => 'c',
+ 2 => 'e',
+)
+ Result: true
+
+Test 6.ab530b060e5e54a65bfb8b9f8fc61870:
+array (
+ 0 => '25',
+ 1 => '36',
+ 2 => '100',
+)
+ Result: true
+
+Test 7.0718dd838509017bded2ed307a6e785f:
+array (
+ 0 => 2,
+ 1 => 5,
+ 2 => '30',
+)
+ Result: true
+
+Test 8.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 9.289bc2f28e87d3201ec9d7e8477ae1b0:
+array (
+ 0 => ' a',
+ 1 => 'd ',
+ 2 => 'f ',
+)
+ Result: true
+
+Test 10.de0fd958484f2377a645835d7fbcf124:
+array (
+ 0 => NULL,
+ 1 => '3',
+ 2 => 'a',
+)
+ Result: true
+
+Test 11.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 12.1e6b4d6f7df9d4580317634ea46d8208:
+array (
+ 0 => '100',
+ 1 => '25',
+ 2 => '36',
+)
+ Result: true
+
+Test 13.cec115dc9850b98dfbdf102efa09e61b:
+array (
+ 0 => 2,
+ 1 => '30',
+ 2 => 5,
+)
+ Result: true
+
+Test 14.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 15.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 16.ca0e38a2e3147dd97070f2128f140934:
+array (
+ 0 => 'abc',
+ 1 => 'ааа',
+ 2 => 'абв',
+ 3 => 'абг',
+)
+ Result: true
+
+Test 17.91480b10473a0c96a4cd6d88c23c577a:
+array (
+ 0 => 'а',
+ 1 => 'аа',
+ 2 => 'ааа',
+)
+ Result: true
+
+Test 18.fdd3fe3981476039164aa000bf9177f2:
+array (
+ 0 => 'i',
+ 1 => 'y',
+ 2 => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/collator_sort_with_sort_keys.phpt b/ext/intl/tests/collator_sort_with_sort_keys.phpt
new file mode 100644
index 0000000..2f489d7
--- /dev/null
+++ b/ext/intl/tests/collator_sort_with_sort_keys.phpt
@@ -0,0 +1,189 @@
+--TEST--
+sort_with_sort_keys()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Sort arrays using various locales.
+ */
+
+
+$test_num = 1;
+
+/*
+ * Sort arrays in the given list using specified locale.
+ */
+function sort_arrays( $locale, $arrays )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ foreach( $arrays as $array )
+ {
+ // Sort array values
+ $res_val = ut_coll_sort_with_sort_keys( $coll, $array );
+
+ // Concatenate the sorted array and function result
+ // with output string.
+ $res_dump = "\n" . dump( $array ) .
+ "\n Result: " . dump( $res_val );
+
+
+ // Preppend test signature to output string
+ $md5 = md5( $res_dump );
+
+ global $test_num;
+
+ $res_str .= "\n\n".
+ "Test $test_num.$md5:" .
+ $res_dump;
+ ++$test_num;
+ }
+
+ return $res_str;
+}
+
+
+function ut_main()
+{
+ global $test_num;
+ $test_num = 1;
+ $res_str = '';
+
+ // Sort an array in SORT_REGULAR mode using en_US locale.
+ $test_params = array(
+ array( 'abc', 'abd', 'aaa' ),
+ array( 'm' , '1' , '_' ),
+ array( 'a' , 'aaa', 'aa' ),
+ array( 'ba' , 'b' , 'ab' ),
+ array( 'e' , 'c' , 'a' ),
+ array( 'd' , '' , ' a' ),
+ array( 'd ' , 'f ' , ' a' ),
+ array( 'a' , null , '3' ),
+ array( 'y' , 'i' , 'k' )
+ );
+
+ $res_str .= sort_arrays( 'en_US', $test_params );
+
+ // Sort a non-ASCII array using ru_RU locale.
+ $test_params = array(
+ array( 'абг', 'абв', 'ааа', 'abc' ),
+ array( 'аа', 'ааа', 'а' )
+ );
+
+ $res_str .= sort_arrays( 'ru_RU', $test_params );
+
+ // Array with data for sorting.
+ $test_params = array(
+ array( 'y' , 'i' , 'k' )
+ );
+
+ // Sort an array using Lithuanian locale.
+ $res_str .= sort_arrays( 'lt_LT', $test_params );
+
+ return $res_str . "\n";
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+Test 1.e8f1cd28133d79ecd660002f1c660d0e:
+array (
+ 0 => 'aaa',
+ 1 => 'abc',
+ 2 => 'abd',
+)
+ Result: true
+
+Test 2.c2ded12173dd2996927378cae37eb275:
+array (
+ 0 => '_',
+ 1 => '1',
+ 2 => 'm',
+)
+ Result: true
+
+Test 3.54071c968d71cb98c5d379145f8d7d38:
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+ Result: true
+
+Test 4.19abe63d6f6dfef65b0e3c9ab4826b07:
+array (
+ 0 => 'ab',
+ 1 => 'b',
+ 2 => 'ba',
+)
+ Result: true
+
+Test 5.9a8dc0a9bc771368c2f1fc3d02754610:
+array (
+ 0 => 'a',
+ 1 => 'c',
+ 2 => 'e',
+)
+ Result: true
+
+Test 6.923d65739c5219c634616ffd100a50e4:
+array (
+ 0 => '',
+ 1 => ' a',
+ 2 => 'd',
+)
+ Result: true
+
+Test 7.289bc2f28e87d3201ec9d7e8477ae1b0:
+array (
+ 0 => ' a',
+ 1 => 'd ',
+ 2 => 'f ',
+)
+ Result: true
+
+Test 8.de0fd958484f2377a645835d7fbcf124:
+array (
+ 0 => NULL,
+ 1 => '3',
+ 2 => 'a',
+)
+ Result: true
+
+Test 9.dd2b8f0adb37c45d528cad1a0cc0f361:
+array (
+ 0 => 'i',
+ 1 => 'k',
+ 2 => 'y',
+)
+ Result: true
+
+Test 10.ca0e38a2e3147dd97070f2128f140934:
+array (
+ 0 => 'abc',
+ 1 => 'ааа',
+ 2 => 'абв',
+ 3 => 'абг',
+)
+ Result: true
+
+Test 11.91480b10473a0c96a4cd6d88c23c577a:
+array (
+ 0 => 'а',
+ 1 => 'аа',
+ 2 => 'ааа',
+)
+ Result: true
+
+Test 12.fdd3fe3981476039164aa000bf9177f2:
+array (
+ 0 => 'i',
+ 1 => 'y',
+ 2 => 'k',
+)
+ Result: true
diff --git a/ext/intl/tests/dateformat_calendars.phpt b/ext/intl/tests/dateformat_calendars.phpt
new file mode 100644
index 0000000..27f380c
--- /dev/null
+++ b/ext/intl/tests/dateformat_calendars.phpt
@@ -0,0 +1,45 @@
+--TEST--
+IntlDateFormatter, calendars and time zone
+--INI--
+date.timezone=Atlantic/Azores
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+
+$fmt1 = new IntlDateFormatter('en_US',
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::FULL,
+ 'GMT+05:12',
+ IntlDateFormatter::TRADITIONAL);
+$fmt2 = new IntlDateFormatter('en_US',
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::FULL,
+ 'GMT+05:12',
+ IntlDateFormatter::GREGORIAN);
+$fmt3 = new IntlDateFormatter('en_US@calendar=hebrew',
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::FULL,
+ 'GMT+05:12',
+ IntlDateFormatter::TRADITIONAL);
+var_dump($fmt1->format(strtotime('2012-01-01 00:00:00 +0000')));
+var_dump($fmt2->format(strtotime('2012-01-01 00:00:00 +0000')));
+var_dump($fmt3->format(strtotime('2012-01-01 00:00:00 +0000')));
+
+new IntlDateFormatter('en_US@calendar=hebrew',
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::FULL,
+ 'GMT+05:12',
+ -1);
+?>
+==DONE==
+--EXPECTF--
+string(44) "Sunday, January 1, 2012 5:12:00 AM GMT+05:12"
+string(44) "Sunday, January 1, 2012 5:12:00 AM GMT+05:12"
+string(42) "Sunday, Tevet 6, 5772 5:12:00 AM GMT+05:12"
+
+Warning: IntlDateFormatter::__construct(): datefmt_create: invalid value for calendar type; it must be one of IntlDateFormatter::TRADITIONAL (locale's default calendar) or IntlDateFormatter::GREGORIAN in %s on line %d
+==DONE==
diff --git a/ext/intl/tests/dateformat_clone.phpt b/ext/intl/tests/dateformat_clone.phpt
new file mode 100644
index 0000000..f060444
--- /dev/null
+++ b/ext/intl/tests/dateformat_clone.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Cloning datefmt icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--FILE--
+<?php
+include_once( 'ut_common.inc' );
+$GLOBALS['oo-mode'] = true;
+$res_str = '';
+/*
+ * Clone
+ */
+$start_pattern = 'dd-MM-YY';
+$fmt = ut_datefmt_create( "en-US", IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/New_York', IntlDateFormatter::GREGORIAN , $start_pattern );
+
+$formatted = ut_datefmt_format($fmt,0);
+$res_str .= "\nResult of formatting timestamp=0 is : \n$formatted";
+
+$fmt_clone = clone $fmt;
+ut_datefmt_set_pattern( $fmt , 'yyyy-DDD.hh:mm:ss z' );
+
+$formatted = ut_datefmt_format($fmt,0);
+$res_str .= "\nResult of formatting timestamp=0 is : \n$formatted";
+$formatted = ut_datefmt_format($fmt_clone,0);
+$res_str .= "\nResult of clone formatting timestamp=0 is : \n$formatted";
+
+echo $res_str;
+
+?>
+--EXPECTF--
+Result of formatting timestamp=0 is :
+31-12-69
+Result of formatting timestamp=0 is :
+1969-365.07:00:00 EST
+Result of clone formatting timestamp=0 is :
+31-12-69
diff --git a/ext/intl/tests/dateformat_clone2.phpt b/ext/intl/tests/dateformat_clone2.phpt
new file mode 100644
index 0000000..c91f4b8
--- /dev/null
+++ b/ext/intl/tests/dateformat_clone2.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Cloning datefmt icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?>
+--FILE--
+<?php
+include_once( 'ut_common.inc' );
+$GLOBALS['oo-mode'] = true;
+$res_str = '';
+/*
+ * Clone
+ */
+$start_pattern = 'dd-MM-YY';
+$fmt = ut_datefmt_create( "en-US", IntlDateFormatter::FULL, IntlDateFormatter::FULL, 'America/New_York', IntlDateFormatter::GREGORIAN , $start_pattern );
+
+$formatted = ut_datefmt_format($fmt,0);
+$res_str .= "\nResult of formatting timestamp=0 is : \n$formatted";
+
+$fmt_clone = clone $fmt;
+ut_datefmt_set_pattern( $fmt , 'yyyy-DDD.hh:mm:ss z' );
+
+$formatted = ut_datefmt_format($fmt,0);
+$res_str .= "\nResult of formatting timestamp=0 is : \n$formatted";
+$formatted = ut_datefmt_format($fmt_clone,0);
+$res_str .= "\nResult of clone formatting timestamp=0 is : \n$formatted";
+
+echo $res_str;
+
+?>
+--EXPECTF--
+Result of formatting timestamp=0 is :
+31-12-70
+Result of formatting timestamp=0 is :
+1969-365.07:00:00 EST
+Result of clone formatting timestamp=0 is :
+31-12-70
diff --git a/ext/intl/tests/dateformat_clone_bad_obj.phpt b/ext/intl/tests/dateformat_clone_bad_obj.phpt
new file mode 100644
index 0000000..5e12b96
--- /dev/null
+++ b/ext/intl/tests/dateformat_clone_bad_obj.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Cloning unconstructed IntlDateFormatter
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+class A extends IntlDateFormatter {
+ function __construct() {}
+}
+
+$a = new A;
+try {
+ $b = clone $a;
+} catch (Exception $e) {
+ var_dump($e->getMessage());
+}
+
+--EXPECTF--
+string(%s) "Cannot clone unconstructed IntlDateFormatter"
diff --git a/ext/intl/tests/dateformat_format.phpt b/ext/intl/tests/dateformat_format.phpt
new file mode 100644
index 0000000..e554819
--- /dev/null
+++ b/ext/intl/tests/dateformat_format.phpt
@@ -0,0 +1,420 @@
+--TEST--
+datefmt_format_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_format function
+ */
+
+
+function ut_main()
+{
+ $timezone = 'GMT-10';
+
+ $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.0,
+ -2200000000.0,
+ 90099999,
+ 3600,
+ -3600
+ );
+
+ $localtime_arr1 = array (
+ 'tm_sec' => 24 ,
+ 'tm_min' => 3,
+ 'tm_hour' => 19,
+ 'tm_mday' => 3,
+ 'tm_mon' => 3,
+ 'tm_year' => 105,
+ );
+ $localtime_arr2 = array (
+ 'tm_sec' => 21,
+ 'tm_min' => 5,
+ 'tm_hour' => 7,
+ 'tm_mday' => 13,
+ 'tm_mon' => 4,
+ 'tm_year' => 205,
+ );
+ $localtime_arr3 = array (
+ 'tm_sec' => 11,
+ 'tm_min' => 13,
+ 'tm_hour' => 0,
+ 'tm_mday' => 17,
+ 'tm_mon' => 11,
+ 'tm_year' => -5
+ );
+
+ $localtime_arr = array (
+ $localtime_arr1,
+ $localtime_arr2,
+ $localtime_arr3
+ );
+
+ $d1 = new DateTime("2010-01-01 01:02:03", new DateTimeZone("UTC"));
+ $d2 = new DateTime("2000-12-31 03:04:05", new DateTimeZone("UTC"));
+ $d2->setTimezone(new DateTimeZone("PDT"));
+ $dates = array(
+ $d1,
+ $d2,
+ new StdClass(),
+ );
+
+ //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, $timezone, IntlDateFormatter::GREGORIAN);
+ $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, $timezone, IntlDateFormatter::GREGORIAN );
+ $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()."'";
+ }
+ }
+ }
+ }
+
+ foreach($dates as $date_entry) {
+ foreach( $locale_arr as $locale_entry ){
+ foreach( $datetype_arr as $datetype_entry ) {
+ $res_str .= "\n------------";
+ $res_str .= "\nDate is: ".var_export($date_entry, true);
+ $res_str .= "\n------------";
+
+ $fmt = ut_datefmt_create( $locale_entry , $datetype_entry ,$datetype_entry, $timezone, IntlDateFormatter::GREGORIAN );
+ $formatted1 = ut_datefmt_format( $fmt , $date_entry);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nFormatted DateTime 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 2:00:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 2:00:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 2:00:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/31/69 2:00 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691231 02:00 PM
+------------
+
+Input timestamp is : -1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 17, 1969 4:40:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 17, 1969 4:40:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 17, 1969 4:40:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/17/69 4:40 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691217 04:40 PM
+------------
+
+Input timestamp is : 1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, January 14, 1970 11:20:00 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 14, 1970 11:20:00 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 14, 1970 11:20:00 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 1/14/70 11:20 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19700114 11:20 AM
+------------
+
+Input timestamp is : 2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Sunday, September 18, 2039 1:06:40 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : September 18, 2039 1:06:40 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Sep 18, 2039 1:06:40 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 9/18/39 1:06 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 20390918 01:06 PM
+------------
+
+Input timestamp is : -2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Saturday, April 14, 1900 2:53:20 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : April 14, 1900 2:53:20 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Apr 14, 1900 2:53:20 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 4/14/00 2:53 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19000414 02:53 PM
+------------
+
+Input timestamp is : 90099999
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, November 8, 1972 9:46:39 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : November 8, 1972 9:46:39 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Nov 8, 1972 9:46:39 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 11/8/72 9:46 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19721108 09:46 AM
+------------
+
+Input timestamp is : 3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 3:00:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 3:00:00 PM GMT-10:00
+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 timestamp is : -3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Wednesday, December 31, 1969 1:00:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 31, 1969 1:00:00 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 31, 1969 1:00:00 PM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted timestamp is : 12/31/69 1:00 PM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted timestamp is : 19691231 01:00 PM
+------------
+
+Input localtime is : tm_sec : '24' , tm_min : '3' , tm_hour : '19' , tm_mday : '3' , tm_mon : '3' , tm_year : '105' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Sunday, April 3, 2005 7:03:24 PM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2005 7:03:24 PM GMT-10:00
+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 : '21' , tm_min : '5' , tm_hour : '7' , tm_mday : '13' , tm_mon : '4' , tm_year : '205' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Wednesday, May 13, 2105 7:05:21 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : May 13, 2105 7:05:21 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : May 13, 2105 7:05:21 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted localtime_array is : 5/13/05 7:05 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted localtime_array is : 21050513 07:05 AM
+------------
+
+Input localtime is : tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_mday : '17' , tm_mon : '11' , tm_year : '-5' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Tuesday, December 17, 1895 12:13:11 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : December 17, 1895 12:13:11 AM GMT-10:00
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Dec 17, 1895 12:13:11 AM
+IntlDateFormatter locale= en_US ,datetype = 3 ,timetype =3
+Formatted localtime_array is : 12/17/95 12:13 AM
+IntlDateFormatter locale= en_US ,datetype = -1 ,timetype =-1
+Formatted localtime_array is : 18951217 12:13 AM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2010-01-01 01:02:03',
+ 'timezone_type' => 3,
+ 'timezone' => 'UTC',
+))
+------------
+Formatted DateTime is : Thursday, December 31, 2009 3:02:03 PM GMT-10:00
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2010-01-01 01:02:03',
+ 'timezone_type' => 3,
+ 'timezone' => 'UTC',
+))
+------------
+Formatted DateTime is : December 31, 2009 3:02:03 PM GMT-10:00
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2010-01-01 01:02:03',
+ 'timezone_type' => 3,
+ 'timezone' => 'UTC',
+))
+------------
+Formatted DateTime is : Dec 31, 2009 3:02:03 PM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2010-01-01 01:02:03',
+ 'timezone_type' => 3,
+ 'timezone' => 'UTC',
+))
+------------
+Formatted DateTime is : 12/31/09 3:02 PM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2010-01-01 01:02:03',
+ 'timezone_type' => 3,
+ 'timezone' => 'UTC',
+))
+------------
+Formatted DateTime is : 20091231 03:02 PM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2000-12-30 19:04:05',
+ 'timezone_type' => 3,
+ 'timezone' => 'America/Los_Angeles',
+))
+------------
+Formatted DateTime is : Saturday, December 30, 2000 5:04:05 PM GMT-10:00
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2000-12-30 19:04:05',
+ 'timezone_type' => 3,
+ 'timezone' => 'America/Los_Angeles',
+))
+------------
+Formatted DateTime is : December 30, 2000 5:04:05 PM GMT-10:00
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2000-12-30 19:04:05',
+ 'timezone_type' => 3,
+ 'timezone' => 'America/Los_Angeles',
+))
+------------
+Formatted DateTime is : Dec 30, 2000 5:04:05 PM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2000-12-30 19:04:05',
+ 'timezone_type' => 3,
+ 'timezone' => 'America/Los_Angeles',
+))
+------------
+Formatted DateTime is : 12/30/00 5:04 PM
+------------
+Date is: DateTime::__set_state(array(
+ 'date' => '2000-12-30 19:04:05',
+ 'timezone_type' => 3,
+ 'timezone' => 'America/Los_Angeles',
+))
+------------
+Formatted DateTime is : 20001230 05:04 PM
+------------
+Date is: stdClass::__set_state(array(
+))
+------------
+Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR'
+------------
+Date is: stdClass::__set_state(array(
+))
+------------
+Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR'
+------------
+Date is: stdClass::__set_state(array(
+))
+------------
+Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR'
+------------
+Date is: stdClass::__set_state(array(
+))
+------------
+Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR'
+------------
+Date is: stdClass::__set_state(array(
+))
+------------
+Error while formatting as: 'datefmt_format: object must be an instance of DateTime: U_ILLEGAL_ARGUMENT_ERROR'
diff --git a/ext/intl/tests/dateformat_format_parse.phpt b/ext/intl/tests/dateformat_format_parse.phpt
new file mode 100644
index 0000000..bd41d71
--- /dev/null
+++ b/ext/intl/tests/dateformat_format_parse.phpt
@@ -0,0 +1,294 @@
+--TEST--
+datefmt_format_code() and datefmt_parse_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_format function
+ */
+
+
+function ut_main()
+{
+ $timezone = 'GMT+5';
+
+ $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,
+ );
+ $localtime_arr2 = array (
+ 'tm_sec' => 21,
+ 'tm_min' => 5,
+ 'tm_hour' => 7,
+ 'tm_mday' => 13,
+ 'tm_mon' => 7,
+ 'tm_year' => 205,
+ );
+ $localtime_arr3 = array (
+ 'tm_sec' => 11,
+ 'tm_min' => 13,
+ 'tm_hour' => 0,
+ 'tm_mday' => 17,
+ 'tm_mon' => 11,
+ 'tm_year' => -5
+ );
+
+ $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,$timezone);
+ $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,$timezone);
+ $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 : Thursday, January 1, 1970 5:00:00 AM GMT+05:00
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 5:00:00 AM GMT+05:00
+Parsed timestamp is : 0
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970 5:00:00 AM
+Parsed timestamp is : 0
+------------
+
+Input timestamp is : -1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, December 18, 1969 7:40:00 AM GMT+05:00
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : December 18, 1969 7:40:00 AM GMT+05:00
+Parsed timestamp is : -1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Dec 18, 1969 7:40:00 AM
+Parsed timestamp is : -1200000
+------------
+
+Input timestamp is : 1200000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 15, 1970 2:20:00 AM GMT+05:00
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 15, 1970 2:20:00 AM GMT+05:00
+Parsed timestamp is : 1200000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 15, 1970 2:20:00 AM
+Parsed timestamp is : 1200000
+------------
+
+Input timestamp is : 2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Monday, September 19, 2039 4:06:40 AM GMT+05:00
+Parsed timestamp is : 2200000000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : September 19, 2039 4:06:40 AM GMT+05:00
+Parsed timestamp is : 2200000000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Sep 19, 2039 4:06:40 AM
+Parsed timestamp is : 2200000000
+------------
+
+Input timestamp is : -2200000000
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Sunday, April 15, 1900 5:53:20 AM GMT+05:00
+Parsed timestamp is : -2200000000
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : April 15, 1900 5:53:20 AM GMT+05:00
+Parsed timestamp is : -2200000000
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Apr 15, 1900 5:53:20 AM
+Parsed timestamp is : -2200000000
+------------
+
+Input timestamp is : 90099999
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, November 9, 1972 12:46:39 AM GMT+05:00
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : November 9, 1972 12:46:39 AM GMT+05:00
+Parsed timestamp is : 90099999
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Nov 9, 1972 12:46:39 AM
+Parsed timestamp is : 90099999
+------------
+
+Input timestamp is : 3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 1, 1970 6:00:00 AM GMT+05:00
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 6:00:00 AM GMT+05:00
+Parsed timestamp is : 3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970 6:00:00 AM
+Parsed timestamp is : 3600
+------------
+
+Input timestamp is : -3600
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted timestamp is : Thursday, January 1, 1970 4:00:00 AM GMT+05:00
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted timestamp is : January 1, 1970 4:00:00 AM GMT+05:00
+Parsed timestamp is : -3600
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted timestamp is : Jan 1, 1970 4:00:00 AM
+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' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Sunday, April 3, 2005 7:03:24 PM GMT+05:00
+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 : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : April 3, 2005 7:03:24 PM GMT+05:00
+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 : '0' ,
+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 : '0' ,
+------------
+
+Input localtime is : tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_mday : '13' , tm_mon : '7' , tm_year : '205' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Thursday, August 13, 2105 7:05:21 AM GMT+05:00
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : August 13, 2105 7:05:21 AM GMT+05:00
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Aug 13, 2105 7:05:21 AM
+Parsed array is: tm_sec : '21' , tm_min : '5' , tm_hour : '7' , tm_year : '205' , tm_mday : '13' , tm_wday : '4' , tm_yday : '225' , tm_mon : '7' , tm_isdst : '0' ,
+------------
+
+Input localtime is : tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_mday : '17' , tm_mon : '11' , tm_year : '-5' ,
+------------
+
+IntlDateFormatter locale= en_US ,datetype = 0 ,timetype =0
+Formatted localtime_array is : Tuesday, December 17, 1895 12:13:11 AM GMT+05:00
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 1 ,timetype =1
+Formatted localtime_array is : December 17, 1895 12:13:11 AM GMT+05:00
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
+IntlDateFormatter locale= en_US ,datetype = 2 ,timetype =2
+Formatted localtime_array is : Dec 17, 1895 12:13:11 AM
+Parsed array is: tm_sec : '11' , tm_min : '13' , tm_hour : '0' , tm_year : '-5' , tm_mday : '17' , tm_wday : '2' , tm_yday : '351' , tm_mon : '11' , tm_isdst : '0' ,
diff --git a/ext/intl/tests/dateformat_get_datetype.phpt b/ext/intl/tests/dateformat_get_datetype.phpt
new file mode 100644
index 0000000..24d0389
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_datetype.phpt
@@ -0,0 +1,57 @@
+--TEST--
+datefmt_get_datetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_datetype function
+ */
+
+
+function ut_main()
+{
+ $datetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+ foreach( $datetype_arr as $datetype_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with date_type = $datetype_entry";
+ $fmt = ut_datefmt_create( "de-DE", $datetype_entry , IntlDateFormatter::SHORT,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $date_type = ut_datefmt_get_datetype( $fmt);
+ $res_str .= "\nAfter call to get_datetype : datetype= $date_type";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with date_type = 0
+After call to get_datetype : datetype= 0
+
+Creating IntlDateFormatter with date_type = 1
+After call to get_datetype : datetype= 1
+
+Creating IntlDateFormatter with date_type = 2
+After call to get_datetype : datetype= 2
+
+Creating IntlDateFormatter with date_type = 3
+After call to get_datetype : datetype= 3
+
+Creating IntlDateFormatter with date_type = -1
+After call to get_datetype : datetype= -1
diff --git a/ext/intl/tests/dateformat_get_locale.phpt b/ext/intl/tests/dateformat_get_locale.phpt
new file mode 100644
index 0000000..17fcca3
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_locale.phpt
@@ -0,0 +1,59 @@
+--TEST--
+datefmt_get_locale_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_locale function
+ */
+
+
+function ut_main()
+{
+ $locale_arr = array (
+ 'de-DE',
+ 'sl-IT-nedis',
+ 'en_UK',
+ 'hi'
+ );
+
+ $res_str = '';
+
+ foreach( $locale_arr as $locale_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with locale = $locale_entry";
+ $fmt = ut_datefmt_create( $locale_entry , IntlDateFormatter::SHORT,IntlDateFormatter::SHORT,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $locale = ut_datefmt_get_locale( $fmt , 1);
+ $res_str .= "\nAfter call to get_locale : locale= $locale";
+ $res_str .= "\n";
+ }
+ $badvals = array(100, -1, 4294901761);
+ foreach($badvals as $badval) {
+ if(ut_datefmt_get_locale($fmt, $badval)) {
+ $res_str .= "datefmt_get_locale should return false for bad argument $badval\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 100644
index 0000000..bfd4e57
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_set_calendar.phpt
@@ -0,0 +1,60 @@
+--TEST--
+datefmt_get_calendar_code() datefmt_set_calendar_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_calendar and datefmt_set_calendar functions
+ */
+
+
+function ut_main()
+{
+ $calendar_arr = array (
+ IntlDateFormatter::GREGORIAN,
+ IntlDateFormatter::TRADITIONAL,
+ 3
+ );
+
+ $res_str = '';
+
+ $start_calendar = IntlDateFormatter::GREGORIAN;
+ $res_str .= "\nCreating IntlDateFormatter with calendar = $start_calendar";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT ,'America/Los_Angeles', IntlDateFormatter::GREGORIAN);
+ $calendar = ut_datefmt_get_calendar( $fmt);
+ $res_str .= "\nAfter call to get_calendar : calendar= $calendar";
+ $res_str .= "\n-------------------";
+
+ foreach( $calendar_arr as $calendar_entry )
+ {
+ $res_str .= "\nSetting IntlDateFormatter with calendar = $calendar_entry";
+ ut_datefmt_set_calendar( $fmt, $calendar_entry);
+ $calendar = ut_datefmt_get_calendar( $fmt);
+ $res_str .= "\nAfter call to get_calendar : calendar= $calendar";
+ $res_str .= "\n-------------------";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with calendar = 1
+After call to get_calendar : calendar= 1
+-------------------
+Setting IntlDateFormatter with calendar = 1
+After call to get_calendar : calendar= 1
+-------------------
+Setting IntlDateFormatter with calendar = 0
+After call to get_calendar : calendar= 0
+-------------------
+Setting IntlDateFormatter with calendar = 3
+After call to get_calendar : calendar= 0
+------------------- \ No newline at end of file
diff --git a/ext/intl/tests/dateformat_get_set_pattern.phpt b/ext/intl/tests/dateformat_get_set_pattern.phpt
new file mode 100644
index 0000000..bd541c1
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_set_pattern.phpt
@@ -0,0 +1,85 @@
+--TEST--
+datefmt_get_pattern_code and datefmt_set_pattern_code() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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-69
+-------------------
+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-1969 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_set_pattern2.phpt b/ext/intl/tests/dateformat_get_set_pattern2.phpt
new file mode 100644
index 0000000..de41f10
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_set_pattern2.phpt
@@ -0,0 +1,85 @@
+--TEST--
+datefmt_get_pattern_code and datefmt_set_pattern_code() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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 100644
index 0000000..2ddc117
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_timetype.phpt
@@ -0,0 +1,57 @@
+--TEST--
+datefmt_get_timetype_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_timetype function
+ */
+
+
+function ut_main()
+{
+ $timetype_arr = array (
+ IntlDateFormatter::FULL,
+ IntlDateFormatter::LONG,
+ IntlDateFormatter::MEDIUM,
+ IntlDateFormatter::SHORT,
+ IntlDateFormatter::NONE
+ );
+
+ $res_str = '';
+
+ foreach( $timetype_arr as $timetype_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with time_type = $timetype_entry";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, $timetype_entry ,'America/Los_Angeles', IntlDateFormatter::GREGORIAN );
+ $time_type = ut_datefmt_get_timetype( $fmt);
+ $res_str .= "\nAfter call to get_timetype : timetype= $time_type";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with time_type = 0
+After call to get_timetype : timetype= 0
+
+Creating IntlDateFormatter with time_type = 1
+After call to get_timetype : timetype= 1
+
+Creating IntlDateFormatter with time_type = 2
+After call to get_timetype : timetype= 2
+
+Creating IntlDateFormatter with time_type = 3
+After call to get_timetype : timetype= 3
+
+Creating IntlDateFormatter with time_type = -1
+After call to get_timetype : timetype= -1
diff --git a/ext/intl/tests/dateformat_get_timezone_id.phpt b/ext/intl/tests/dateformat_get_timezone_id.phpt
new file mode 100644
index 0000000..80cbdbb
--- /dev/null
+++ b/ext/intl/tests/dateformat_get_timezone_id.phpt
@@ -0,0 +1,49 @@
+--TEST--
+datefmt_get_timezone_id_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_get_timezone_id function
+ */
+
+
+function ut_main()
+{
+ $timezone_id_arr = array (
+ 'America/New_York',
+ 'America/Los_Angeles',
+ 'America/Dallas'
+ );
+
+ $res_str = '';
+
+ foreach( $timezone_id_arr as $timezone_id_entry )
+ {
+ $res_str .= "\nCreating IntlDateFormatter with timezone_id = $timezone_id_entry";
+ $fmt = ut_datefmt_create( "de-DE", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, $timezone_id_entry , IntlDateFormatter::GREGORIAN );
+ $timezone_id = ut_datefmt_get_timezone_id( $fmt);
+ $res_str .= "\nAfter call to get_timezone_id : timezone_id= $timezone_id";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+Creating IntlDateFormatter with timezone_id = America/New_York
+After call to get_timezone_id : timezone_id= America/New_York
+
+Creating IntlDateFormatter with timezone_id = America/Los_Angeles
+After call to get_timezone_id : timezone_id= America/Los_Angeles
+
+Creating IntlDateFormatter with timezone_id = America/Dallas
+After call to get_timezone_id : timezone_id= America/Dallas
diff --git a/ext/intl/tests/dateformat_is_set_lenient.phpt b/ext/intl/tests/dateformat_is_set_lenient.phpt
new file mode 100644
index 0000000..8492a2e
--- /dev/null
+++ b/ext/intl/tests/dateformat_is_set_lenient.phpt
@@ -0,0 +1,88 @@
+--TEST--
+datefmt_set_lenient and datefmt_set_lenient()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+
+<?php
+
+/*
+ * Test for the datefmt_get_lenient & datefmt_set_lenient function
+ */
+
+
+function ut_main()
+{
+
+ $res_str = '';
+
+ //Create
+ $fmt = ut_datefmt_create( "en-US", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT , 'America/New_York', IntlDateFormatter::GREGORIAN );
+ $res_str .= "\nIntlDateFormatter Created.\n";
+
+ $resLenient1 = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to get_lenient : lenient= ";
+ if( $resLenient1){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+ //Set and test
+ $res_str .= "--------------------\n";
+ $isLenient = TRUE;
+ $res_str .= "Setting IntlDateFormatter with lenient = ";
+ if( $isLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+ ut_datefmt_set_lenient( $fmt , $isLenient );
+ $resLenient = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to is_lenient : lenient= ";
+ if( $resLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+
+ //Set and test
+ $res_str .= "--------------------\n";
+ $isLenient = FALSE;
+ $res_str .= "Setting IntlDateFormatter with lenient =";
+ if( $isLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+ ut_datefmt_set_lenient( $fmt , $isLenient);
+ $resLenient = ut_datefmt_is_lenient( $fmt);
+ $res_str .= "After call to is_lenient : lenient= ";
+ if( $resLenient){
+ $res_str .= "TRUE\n";
+ }else{
+ $res_str .= "FALSE\n";
+ }
+
+ $res_str .= "--------------------\n";
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+IntlDateFormatter Created.
+After call to get_lenient : lenient= TRUE
+--------------------
+Setting IntlDateFormatter with lenient = TRUE
+After call to is_lenient : lenient= TRUE
+--------------------
+Setting IntlDateFormatter with lenient =FALSE
+After call to is_lenient : lenient= FALSE
+--------------------
diff --git a/ext/intl/tests/dateformat_localtime.phpt b/ext/intl/tests/dateformat_localtime.phpt
new file mode 100644
index 0000000..79f297d
--- /dev/null
+++ b/ext/intl/tests/dateformat_localtime.phpt
@@ -0,0 +1,134 @@
+--TEST--
+datefmt_localtime_code() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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;
+
+
+ foreach( $fmt_array as $fmt_entry ){
+ $res_str .= "\n------------";
+ $res_str .= "\nIntlDateFormatter : ".$fmt_desc_array[$cnt];
+ $parse_pos = 0;
+ $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();
+?>
+--EXPECTF--
+-------------------------------
+
+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 : '%d' ,
+------------
+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 100644
index 0000000..ff59773
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse.phpt
@@ -0,0 +1,113 @@
+--TEST--
+datefmt_parse_code() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--INI--
+date.timezone="America/Los_Angeles"
+--FILE--
+<?php
+
+/*
+ * Test for the datefmt_parse function
+ */
+
+putenv('TZ=America/Los_Angeles');
+
+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 (
+ // Full parsing
+ array("Sunday, September 18, 2039 4:06:40 PM PT", IntlDateFormatter::FULL, IntlDateFormatter::FULL),
+ array("Wednesday, December 17, 1969 6:40:00 PM PT", IntlDateFormatter::FULL, IntlDateFormatter::FULL),
+ array("Thursday, December 18, 1969 8:49:59 PM PST", IntlDateFormatter::FULL, IntlDateFormatter::FULL),
+ array("December 18, 1969 8:49:59 AM PST", IntlDateFormatter::LONG, IntlDateFormatter::FULL),
+ array("12/18/69 8:49 AM", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT),
+ array("19691218 08:49 AM", IntlDateFormatter::SHORT, IntlDateFormatter::SHORT),
+ // Partial parsing
+ array("Sunday, September 18, 2039 4:06:40 PM PT", IntlDateFormatter::FULL, IntlDateFormatter::NONE),
+ array("Sunday, September 18, 2039 4:06:40 PM PT", IntlDateFormatter::FULL, IntlDateFormatter::SHORT),
+ array("December 18, 1969 8:49:59 AM PST", IntlDateFormatter::LONG, IntlDateFormatter::NONE),
+ array("December 18, 1969 8:49:59 AM PST", IntlDateFormatter::LONG, IntlDateFormatter::SHORT),
+ array("12/18/69 8:49 AM", IntlDateFormatter::SHORT, IntlDateFormatter::LONG),
+ array("19691218 08:49 AM", IntlDateFormatter::SHORT, IntlDateFormatter::LONG),
+ );
+
+ foreach( $text_arr as $text_entry){
+ $fmt = ut_datefmt_create( 'en_US_CA', $text_entry[1], $text_entry[2]);
+ $parse_pos = 0;
+ $parsed = ut_datefmt_parse( $fmt , $text_entry[0] , $parse_pos );
+
+ $res_str .= "\nInput text : {$text_entry[0]} ; DF = {$text_entry[1]}; TF = {$text_entry[2]}";
+ if( intl_get_error_code() != U_ZERO_ERROR) {
+ $res_str .= "\nError : ".intl_get_error_message();
+ }
+ $res_str .= "\nParsed: $parsed; parse_pos : $parse_pos\n";
+ }
+
+ 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 ; DF = 0; TF = 0
+Parsed: 2200000000; parse_pos : 40
+
+Input text : Wednesday, December 17, 1969 6:40:00 PM PT ; DF = 0; TF = 0
+Parsed: -1200000; parse_pos : 42
+
+Input text : Thursday, December 18, 1969 8:49:59 PM PST ; DF = 0; TF = 0
+Parsed: -1105801; parse_pos : 42
+
+Input text : December 18, 1969 8:49:59 AM PST ; DF = 1; TF = 0
+Parsed: -1149001; parse_pos : 32
+
+Input text : 12/18/69 8:49 AM ; DF = 3; TF = 3
+Parsed: -1149060; parse_pos : 16
+
+Input text : 19691218 08:49 AM ; DF = 3; TF = 3
+Error : Date parsing failed: U_PARSE_ERROR
+Parsed: ; parse_pos : 8
+
+Input text : Sunday, September 18, 2039 4:06:40 PM PT ; DF = 0; TF = -1
+Parsed: 2199942000; parse_pos : 26
+
+Input text : Sunday, September 18, 2039 4:06:40 PM PT ; DF = 0; TF = 3
+Error : Date parsing failed: U_PARSE_ERROR
+Parsed: ; parse_pos : 31
+
+Input text : December 18, 1969 8:49:59 AM PST ; DF = 1; TF = -1
+Parsed: -1180800; parse_pos : 17
+
+Input text : December 18, 1969 8:49:59 AM PST ; DF = 1; TF = 3
+Error : Date parsing failed: U_PARSE_ERROR
+Parsed: ; parse_pos : 22
+
+Input text : 12/18/69 8:49 AM ; DF = 3; TF = 1
+Error : Date parsing failed: U_PARSE_ERROR
+Parsed: ; parse_pos : 13
+
+Input text : 19691218 08:49 AM ; DF = 3; TF = 1
+Error : Date parsing failed: U_PARSE_ERROR
+Parsed: ; parse_pos : 8
diff --git a/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt b/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt
new file mode 100644
index 0000000..03f99f1
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse_localtime_parsepos.phpt
@@ -0,0 +1,120 @@
+--TEST--
+datefmt_parse_localtime() with parse pos icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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();
+?>
+--EXPECTF--
+-------------------------------
+
+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 : '%d' ,
+------------
+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 100644
index 0000000..0f26643
--- /dev/null
+++ b/ext/intl/tests/dateformat_parse_timestamp_parsepos.phpt
@@ -0,0 +1,157 @@
+--TEST--
+datefmt_parse_timestamp_code() with parse pos icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--INI--
+datetime.timezone="America/Los_Angeles"
+--ENV--
+TZ=America/Los_Angeles
+--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",
+ "20111218 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);
+ $pos = 0;
+ $parsed = ut_datefmt_parse( $fmt , $text_entry, $pos);
+ if( intl_get_error_code() == U_ZERO_ERROR){
+ $res_str .= "\nParsed text is : $parsed; Position = $pos";
+ }else{
+ $res_str .= "\nError while parsing as: '".intl_get_error_message()."'; Position = $pos";
+ }
+ }
+ }
+ }
+ $res_str .= "\n";
+
+ 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
+Parsed text is : 33756908800; Position = 40
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = -1 ,timetype =-1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+------------
+
+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; Position = 42
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = -1 ,timetype =-1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+------------
+
+Input text is : 12/18/69 8:49 AM
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 3 ,timetype =3
+Parsed text is : -1149060; Position = 16
+datetype = -1 ,timetype =-1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+------------
+
+Input text is : 20111218 08:49 AM
+------------
+Locale is : en_US_CA
+------------
+datetype = 0 ,timetype =0
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 8
+datetype = -1 ,timetype =-1
+Parsed text is : 1324226940; Position = 17
+------------
+
+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'; Position = 0
+datetype = 1 ,timetype =1
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 2 ,timetype =2
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 0
+datetype = 3 ,timetype =3
+Error while parsing as: 'Date parsing failed: U_PARSE_ERROR'; Position = 8
+datetype = -1 ,timetype =-1
+Parsed text is : -1149060; Position = 17
diff --git a/ext/intl/tests/dateformat_set_timezone_id.phpt b/ext/intl/tests/dateformat_set_timezone_id.phpt
new file mode 100644
index 0000000..72a9230
--- /dev/null
+++ b/ext/intl/tests/dateformat_set_timezone_id.phpt
@@ -0,0 +1,76 @@
+--TEST--
+datefmt_set_timezone_id_code() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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 Eastern Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 8:00:00 PM Eastern Standard Time
+-----------
+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 Pacific Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 5:00:00 PM Pacific Standard Time
+-----------
+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 Central Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 7:00:00 PM Central Standard Time
+-----------
+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/dateformat_set_timezone_id2.phpt b/ext/intl/tests/dateformat_set_timezone_id2.phpt
new file mode 100644
index 0000000..23aacda
--- /dev/null
+++ b/ext/intl/tests/dateformat_set_timezone_id2.phpt
@@ -0,0 +1,76 @@
+--TEST--
+datefmt_set_timezone_id_code() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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 Eastern Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 8:00:00 PM Eastern Standard Time
+-----------
+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 Pacific Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 5:00:00 PM Pacific Standard Time
+-----------
+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 Central Standard Time
+Formatting timestamp=3600 resulted in Wednesday, December 31, 1969 7:00:00 PM Central Standard Time
+-----------
+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
+Formatting timestamp=3600 resulted in Thursday, January 1, 1970 1:00:00 AM GMT
diff --git a/ext/intl/tests/formatter_clone.phpt b/ext/intl/tests/formatter_clone.phpt
new file mode 100644
index 0000000..4e8d096
--- /dev/null
+++ b/ext/intl/tests/formatter_clone.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Cloning numfmt
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+include_once( 'ut_common.inc' );
+$GLOBALS['oo-mode'] = true;
+$res_str = '';
+/*
+ * Clone
+ */
+$test_value = 12345.123456;
+$fmt = new NumberFormatter( "en_US", NumberFormatter::PATTERN_DECIMAL );
+$res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+$fmt_clone = clone $fmt;
+
+$res = $fmt->setPattern("0.0" );
+if( $res === false )
+ $res_str .= ut_nfmt_get_error_message( $fmt ) . " (" . ut_nfmt_get_error_code( $fmt ) . ")\n";
+
+$res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+$res_str .= "Formatted(clone) number: " . ut_nfmt_format( $fmt_clone, $test_value ) . "\n";
+echo $res_str;
+
+?>
+--EXPECTF--
+Formatted number: 12345.123456
+Formatted number: 12345.1
+Formatted(clone) number: 12345.123456
diff --git a/ext/intl/tests/formatter_clone_bad_obj.phpt b/ext/intl/tests/formatter_clone_bad_obj.phpt
new file mode 100644
index 0000000..ef7b28a
--- /dev/null
+++ b/ext/intl/tests/formatter_clone_bad_obj.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Cloning unconstructed numfmt
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+class A extends NumberFormatter {
+ function __construct() {}
+}
+
+$a = new A;
+try {
+ $b = clone $a;
+} catch (Exception $e) {
+ var_dump($e->getMessage());
+}
+
+--EXPECTF--
+string(42) "Cannot clone unconstructed NumberFormatter"
diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt
new file mode 100644
index 0000000..295f011
--- /dev/null
+++ b/ext/intl/tests/formatter_fail.phpt
@@ -0,0 +1,79 @@
+--TEST--
+numfmt creation failures
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function err($fmt) {
+ if(!$fmt) {
+ echo var_export(intl_get_error_message(), true)."\n";
+ }
+}
+
+function crt($t, $l, $s) {
+ switch(true) {
+ case $t == "O":
+ return new NumberFormatter($l, $s);
+ break;
+ case $t == "C":
+ return NumberFormatter::create($l, $s);
+ break;
+ case $t == "P":
+ return numfmt_create($l, $s);
+ break;
+ }
+}
+
+$args = array(
+ array(null, null),
+ array("whatever", 1234567),
+ array(array(), array()),
+ array("en", -1),
+ array("en_US", NumberFormatter::PATTERN_RULEBASED),
+);
+
+$fmt = new NumberFormatter();
+err($fmt);
+$fmt = numfmt_create();
+err($fmt);
+$fmt = NumberFormatter::create();
+err($fmt);
+
+foreach($args as $arg) {
+ $fmt = crt("O", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("C", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("P", $arg[0], $arg[1]);
+ err($fmt);
+}
+
+?>
+--EXPECTF--
+Warning: NumberFormatter::__construct() expects at least 2 parameters, 0 given in %s on line %d
+'numfmt_create: unable to parse input parameters: 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'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+'numfmt_create: number formatter creation failed: U_UNSUPPORTED_ERROR'
+
+Warning: NumberFormatter::__construct() expects parameter 1 to be string, array given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: NumberFormatter::create() expects parameter 1 to be string, array given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: numfmt_create() expects parameter 1 to be string, array given in %s on line %d
+'numfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'numfmt_create: 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'
+'numfmt_create: 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 100644
index 0000000..334ef49
--- /dev/null
+++ b/ext/intl/tests/formatter_format.phpt
@@ -0,0 +1,130 @@
+--TEST--
+numfmt_format() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+/*
+ * TODO: doesn't pass on ICU 3.6 because 'ru' and 'de' locales changed
+ * currency and percent formatting.
+ */
+
+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
+ );
+
+ $integer = array(
+ NumberFormatter::ORDINAL => '',
+ NumberFormatter::DURATION => '',
+ );
+ $locales = array(
+ 'en_US',
+ 'ru_UA',
+ 'de',
+ 'fr',
+ 'en_UK'
+ );
+
+ $str_res = '';
+ $number = 1234567.891234567890000;
+
+ foreach( $locales as $locale )
+ {
+ $str_res .= "\nLocale 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( isset($integer[$style])?ut_nfmt_format( $fmt, $number, NumberFormatter::TYPE_INT32):ut_nfmt_format( $fmt, $number ) ) . "\n";
+ }
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECTREGEX--
+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,567(th|ᵗʰ)'
+'342:56:07'
+'#####.###'
+Bad formatter!
+
+Locale is: ru_UA
+'1234567,89123457'
+'1 234 567,891'
+'1 234 567,89 ?(грн\.|₴)'
+'123 456 789 ?%'
+'1,23456789123457E6'
+'один миллион двасти тридцать четыре тысяч пятьсот шестьдесят семь запятая восемь девять один два три четыре пять семь'
+'1 234 567.?'
+'1 234 567'
+'#####.###'
+Bad formatter!
+
+Locale is: de
+'1234567,89123457'
+'1.234.567,891'
+'(¤ )?1.234.567,89( ¤)?'
+'123\.456\.789 %'
+'1,23456789123457E6'
+'eine Million zwei­hundert­vier­und­dreißig­tausend­fünf­hundert­sieben­und­sechzig Komma acht neun eins zwei drei vier fünf sieben'
+'1.234.567.?'
+'1.234.567'
+'#####.###'
+Bad formatter!
+
+Locale is: fr
+'1234567,89123457'
+'1 234 567,891'
+'1 234 567,89 ¤'
+'123 456 789 ?%'
+'1,23456789123457E6'
+'un million deux-cent-trente-quatre-mille-cinq-cent-soixante-sept virgule huit neuf un deux trois quatre cinq sept'
+'1 234 567ᵉ?'
+'1 234 567'
+'#####.###'
+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,567(th|ᵗʰ)'
+'342:56:07'
+'#####.###'
+Bad formatter!
diff --git a/ext/intl/tests/formatter_format2.phpt b/ext/intl/tests/formatter_format2.phpt
new file mode 100644
index 0000000..0732e41
--- /dev/null
+++ b/ext/intl/tests/formatter_format2.phpt
@@ -0,0 +1,130 @@
+--TEST--
+numfmt_format() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+/*
+ * TODO: doesn't pass on ICU 3.6 because 'ru' and 'de' locales changed
+ * currency and percent formatting.
+ */
+
+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
+ );
+
+ $integer = array(
+ NumberFormatter::ORDINAL => '',
+ NumberFormatter::DURATION => '',
+ );
+ $locales = array(
+ 'en_US',
+ 'ru_UA',
+ 'de',
+ 'fr',
+ 'en_UK'
+ );
+
+ $str_res = '';
+ $number = 1234567.891234567890000;
+
+ foreach( $locales as $locale )
+ {
+ $str_res .= "\nLocale 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( isset($integer[$style])?ut_nfmt_format( $fmt, $number, NumberFormatter::TYPE_INT32):ut_nfmt_format( $fmt, $number ) ) . "\n";
+ }
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECTREGEX--
+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,567(th|ᵗʰ)'
+'342:56:07'
+'#####.###'
+Bad formatter!
+
+Locale is: ru_UA
+'1234567,89123457'
+'1 234 567,891'
+'1 234 567,89 ?(грн\.|₴)'
+'123 456 789 ?%'
+'1,23456789123457E6'
+'один миллион двести тридцать четыре тысяч пятьсот шестьдесят семь запятая восемь девять один два три четыре пять семь'
+'1 234 567.?'
+'1 234 567'
+'#####.###'
+Bad formatter!
+
+Locale is: de
+'1234567,89123457'
+'1.234.567,891'
+'(¤ )?1.234.567,89( ¤)?'
+'123\.456\.789 %'
+'1,23456789123457E6'
+'eine Million zwei­hundert­vier­und­dreißig­tausend­fünf­hundert­sieben­und­sechzig Komma acht neun eins zwei drei vier fünf sieben'
+'1.234.567.?'
+'1.234.567'
+'#####.###'
+Bad formatter!
+
+Locale is: fr
+'1234567,89123457'
+'1 234 567,891'
+'1 234 567,89 ¤'
+'123 456 789 ?%'
+'1,23456789123457E6'
+'un million deux-cent-trente-quatre-mille-cinq-cent-soixante-sept virgule huit neuf un deux trois quatre cinq sept'
+'1 234 567e'
+'1 234 567'
+'#####.###'
+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,567(th|ᵗʰ)'
+'342:56:07'
+'#####.###'
+Bad formatter!
diff --git a/ext/intl/tests/formatter_format_conv.phpt b/ext/intl/tests/formatter_format_conv.phpt
new file mode 100644
index 0000000..e1d25ef
--- /dev/null
+++ b/ext/intl/tests/formatter_format_conv.phpt
@@ -0,0 +1,24 @@
+--TEST--
+numfmt_format() with type conversion
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+function ut_main()
+{
+ $fmt = ut_nfmt_create( 'en_US', NumberFormatter::DECIMAL );
+ $number = 1234567.891234567890000;
+
+ $str_res = ut_nfmt_format ($fmt, $number, NumberFormatter::TYPE_INT32)."\n";
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+1,234,567
diff --git a/ext/intl/tests/formatter_format_currency.phpt b/ext/intl/tests/formatter_format_currency.phpt
new file mode 100644
index 0000000..be219a5
--- /dev/null
+++ b/ext/intl/tests/formatter_format_currency.phpt
@@ -0,0 +1,50 @@
+--TEST--
+numfmt_format_currency() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc currencies/locales.
+ */
+/*
+ * TODO: doesn't pass on ICU 3.6 because 'ru' and 'uk' locales changed
+ * currency formatting.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK' => 'GBP',
+ 'en_US' => 'USD',
+ 'ru' => 'RUR',
+ 'uk' => 'UAH',
+ 'en' => 'UAH'
+ );
+
+ $res_str = '';
+ $number = 1234567.89;
+
+ foreach( $locales as $locale => $currency )
+ {
+ $fmt = ut_nfmt_create( $locale, NumberFormatter::CURRENCY );
+ $res_str .= "$locale: " . var_export( ut_nfmt_format_currency( $fmt, $number, $currency ), true ) . "\n";
+ }
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+en_UK: '£1,234,567.89'
+en_US: '$1,234,567.89'
+ru: '1 234 567,89 р.'
+uk: '1 234 567,89 ₴'
+en: '₴1,234,567.89'
diff --git a/ext/intl/tests/formatter_format_currency2.phpt b/ext/intl/tests/formatter_format_currency2.phpt
new file mode 100644
index 0000000..d9277f3
--- /dev/null
+++ b/ext/intl/tests/formatter_format_currency2.phpt
@@ -0,0 +1,50 @@
+--TEST--
+numfmt_format_currency() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc currencies/locales.
+ */
+/*
+ * TODO: doesn't pass on ICU 3.6 because 'ru' and 'uk' locales changed
+ * currency formatting.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK' => 'GBP',
+ 'en_US' => 'USD',
+ 'ru' => 'RUR',
+ 'uk' => 'UAH',
+ 'en' => 'UAH'
+ );
+
+ $res_str = '';
+ $number = 1234567.89;
+
+ foreach( $locales as $locale => $currency )
+ {
+ $fmt = ut_nfmt_create( $locale, NumberFormatter::CURRENCY );
+ $res_str .= "$locale: " . var_export( ut_nfmt_format_currency( $fmt, $number, $currency ), true ) . "\n";
+ }
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+en_UK: '£1,234,567.89'
+en_US: '$1,234,567.89'
+ru: '1 234 567,89 р.'
+uk: '1 234 567,89 ₴'
+en: 'UAH1,234,567.89'
diff --git a/ext/intl/tests/formatter_get_error.phpt b/ext/intl/tests/formatter_get_error.phpt
new file mode 100644
index 0000000..acb683e
--- /dev/null
+++ b/ext/intl/tests/formatter_get_error.phpt
@@ -0,0 +1,31 @@
+--TEST--
+numfmt_get_error_message/code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Error handling.
+ */
+
+
+function ut_main()
+{
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $currency = '';
+ $pos = 0;
+ $num = ut_nfmt_parse_currency( $fmt, '123.45', $currency, $pos );
+ if( $num === false )
+ return $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n";
+ else
+ return "Ooops, an error should have occurred.";
+}
+
+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 100644
index 0000000..3d4fb2a
--- /dev/null
+++ b/ext/intl/tests/formatter_get_locale.phpt
@@ -0,0 +1,49 @@
+--TEST--
+numfmt_get_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get locale.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK',
+ 'en_US@California',
+ 'fr_CA',
+ );
+
+ $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'
+fr_CA: actual='fr_CA' valid='fr_CA'
diff --git a/ext/intl/tests/formatter_get_set_attribute.phpt b/ext/intl/tests/formatter_get_set_attribute.phpt
new file mode 100644
index 0000000..51c7d36
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_attribute.phpt
@@ -0,0 +1,194 @@
+--TEST--
+numfmt_get/set_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.2', '<') != 1) print 'skip for ICU 4.4+'; ?>
+--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, 1, 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, 2, 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: 1 ; Format result: '0,012,345.12345' ; Parse result: 12345.12345
+
+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: 2 ; 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 100644
index 0000000..0ae5b30
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_pattern.phpt
@@ -0,0 +1,52 @@
+--TEST--
+numfmt_get/set_pattern()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set pattern.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+ $test_value = 12345.123456;
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::PATTERN_DECIMAL );
+
+ // Get default patten.
+ $res_str .= "Default pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatting result: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ // Set a new pattern.
+ $res = ut_nfmt_set_pattern( $fmt, "0.0" );
+ if( $res === false )
+ $res_str .= ut_nfmt_get_error_message( $fmt ) . " (" . ut_nfmt_get_error_code( $fmt ) . ")\n";
+
+ // Check if the pattern has been changed.
+ $res = ut_nfmt_get_pattern( $fmt );
+ if( $res === false )
+ $res_str .= ut_nfmt_get_error_message( $fmt ) . " (" . ut_nfmt_get_error_code( $fmt ) . ")\n";
+ $res_str .= "New pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ ut_nfmt_set_pattern($fmt, str_repeat('@', 200));
+ $res_str .= "New pattern: '" . ut_nfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted number: " . ut_nfmt_format( $fmt, $test_value ) . "\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTF--
+Default pattern: '#.####################################################################################################################################################################################################################################################################################################################%s'
+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 100644
index 0000000..050e8cf
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_symbol.phpt
@@ -0,0 +1,190 @@
+--TEST--
+numfmt_get/set_symbol() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set symbol.
+ */
+
+
+function ut_main()
+{
+ $longstr = str_repeat("blah", 10);
+ $symbols = array(
+ 'DECIMAL_SEPARATOR_SYMBOL' => array( NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, '_._', 12345.123456, NumberFormatter::DECIMAL ),
+ 'GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '_,_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PATTERN_SEPARATOR_SYMBOL' => array( NumberFormatter::PATTERN_SEPARATOR_SYMBOL, '_;_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PERCENT_SYMBOL' => array( NumberFormatter::PERCENT_SYMBOL, '_%_', 12345.123456, NumberFormatter::PERCENT ),
+ 'ZERO_DIGIT_SYMBOL' => array( NumberFormatter::ZERO_DIGIT_SYMBOL, '_ZD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'DIGIT_SYMBOL' => array( NumberFormatter::DIGIT_SYMBOL, '_DS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MINUS_SIGN_SYMBOL' => array( NumberFormatter::MINUS_SIGN_SYMBOL, '_-_', -12345.123456, NumberFormatter::DECIMAL ),
+ 'PLUS_SIGN_SYMBOL' => array( NumberFormatter::PLUS_SIGN_SYMBOL, '_+_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'CURRENCY_SYMBOL' => array( NumberFormatter::CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'INTL_CURRENCY_SYMBOL' => array( NumberFormatter::INTL_CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_SEPARATOR_SYMBOL, '_MS_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'EXPONENTIAL_SYMBOL' => array( NumberFormatter::EXPONENTIAL_SYMBOL, '_E_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'PERMILL_SYMBOL' => array( NumberFormatter::PERMILL_SYMBOL, '_PS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PAD_ESCAPE_SYMBOL' => array( NumberFormatter::PAD_ESCAPE_SYMBOL, '_PE_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'INFINITY_SYMBOL' => array( NumberFormatter::INFINITY_SYMBOL, '_IS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'NAN_SYMBOL' => array( NumberFormatter::NAN_SYMBOL, '_N_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'SIGNIFICANT_DIGIT_SYMBOL' => array( NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL, '_SD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, '_MG_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-2' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, "&nbsp;", 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-3' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, $longstr, 12345.123456, NumberFormatter::CURRENCY ),
+ );
+
+ $res_str = '';
+
+ foreach( $symbols as $symb_name => $data )
+ {
+ list( $symb, $new_val, $number, $attr ) = $data;
+
+ $fmt = ut_nfmt_create( 'en_US', $attr);
+
+ $res_str .= "\nSymbol '$symb_name'\n";
+
+ // Get original symbol value.
+ $orig_val = ut_nfmt_get_symbol( $fmt, $symb );
+ $res_str .= "Default symbol: [$orig_val]\n";
+
+ // Set a new symbol value.
+ $res_val = ut_nfmt_set_symbol( $fmt, $symb, $new_val );
+ if( !$res_val )
+ $res_str .= "set_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ // Get the symbol value back.
+ $new_val_check = ut_nfmt_get_symbol( $fmt, $symb );
+ if( !$new_val_check )
+ $res_str .= "get_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ $res_str .= "New symbol: [$new_val_check]\n";
+
+ // Check if the new value has been set.
+ if( $new_val_check !== $new_val )
+ $res_str .= "ERROR: New $symb_name symbol value has not been set correctly.\n";
+
+ // Format the number using the new value.
+ $s = ut_nfmt_format( $fmt, $number );
+ $res_str .= "A number formatted with the new symbol: $s\n";
+
+ // Restore attribute's symbol.
+ ut_nfmt_set_symbol( $fmt, $symb, $orig_val );
+ }
+ $badvals = array(2147483648, -2147483648, -1, 4294901761);
+ foreach($badvals as $badval) {
+ if(ut_nfmt_get_symbol( $fmt, 2147483648 )) {
+ $res_str .= "Bad value $badval should return false!\n";
+ }
+ }
+ 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_symbol2.phpt b/ext/intl/tests/formatter_get_set_symbol2.phpt
new file mode 100644
index 0000000..769713b
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_symbol2.phpt
@@ -0,0 +1,190 @@
+--TEST--
+numfmt_get/set_symbol() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set symbol.
+ */
+
+
+function ut_main()
+{
+ $longstr = str_repeat("blah", 10);
+ $symbols = array(
+ 'DECIMAL_SEPARATOR_SYMBOL' => array( NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, '_._', 12345.123456, NumberFormatter::DECIMAL ),
+ 'GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '_,_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PATTERN_SEPARATOR_SYMBOL' => array( NumberFormatter::PATTERN_SEPARATOR_SYMBOL, '_;_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PERCENT_SYMBOL' => array( NumberFormatter::PERCENT_SYMBOL, '_%_', 12345.123456, NumberFormatter::PERCENT ),
+ 'ZERO_DIGIT_SYMBOL' => array( NumberFormatter::ZERO_DIGIT_SYMBOL, '_ZD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'DIGIT_SYMBOL' => array( NumberFormatter::DIGIT_SYMBOL, '_DS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MINUS_SIGN_SYMBOL' => array( NumberFormatter::MINUS_SIGN_SYMBOL, '_-_', -12345.123456, NumberFormatter::DECIMAL ),
+ 'PLUS_SIGN_SYMBOL' => array( NumberFormatter::PLUS_SIGN_SYMBOL, '_+_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'CURRENCY_SYMBOL' => array( NumberFormatter::CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'INTL_CURRENCY_SYMBOL' => array( NumberFormatter::INTL_CURRENCY_SYMBOL, '_$_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_SEPARATOR_SYMBOL, '_MS_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'EXPONENTIAL_SYMBOL' => array( NumberFormatter::EXPONENTIAL_SYMBOL, '_E_', 12345.123456, NumberFormatter::SCIENTIFIC ),
+ 'PERMILL_SYMBOL' => array( NumberFormatter::PERMILL_SYMBOL, '_PS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'PAD_ESCAPE_SYMBOL' => array( NumberFormatter::PAD_ESCAPE_SYMBOL, '_PE_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'INFINITY_SYMBOL' => array( NumberFormatter::INFINITY_SYMBOL, '_IS_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'NAN_SYMBOL' => array( NumberFormatter::NAN_SYMBOL, '_N_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'SIGNIFICANT_DIGIT_SYMBOL' => array( NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL, '_SD_', 12345.123456, NumberFormatter::DECIMAL ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, '_MG_', 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-2' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, "&nbsp;", 12345.123456, NumberFormatter::CURRENCY ),
+ 'MONETARY_GROUPING_SEPARATOR_SYMBOL-3' => array( NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, $longstr, 12345.123456, NumberFormatter::CURRENCY ),
+ );
+
+ $res_str = '';
+
+ foreach( $symbols as $symb_name => $data )
+ {
+ list( $symb, $new_val, $number, $attr ) = $data;
+
+ $fmt = ut_nfmt_create( 'en_US', $attr);
+
+ $res_str .= "\nSymbol '$symb_name'\n";
+
+ // Get original symbol value.
+ $orig_val = ut_nfmt_get_symbol( $fmt, $symb );
+ $res_str .= "Default symbol: [$orig_val]\n";
+
+ // Set a new symbol value.
+ $res_val = ut_nfmt_set_symbol( $fmt, $symb, $new_val );
+ if( !$res_val )
+ $res_str .= "set_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ // Get the symbol value back.
+ $new_val_check = ut_nfmt_get_symbol( $fmt, $symb );
+ if( !$new_val_check )
+ $res_str .= "get_symbol() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ $res_str .= "New symbol: [$new_val_check]\n";
+
+ // Check if the new value has been set.
+ if( $new_val_check !== $new_val )
+ $res_str .= "ERROR: New $symb_name symbol value has not been set correctly.\n";
+
+ // Format the number using the new value.
+ $s = ut_nfmt_format( $fmt, $number );
+ $res_str .= "A number formatted with the new symbol: $s\n";
+
+ // Restore attribute's symbol.
+ ut_nfmt_set_symbol( $fmt, $symb, $orig_val );
+ }
+ $badvals = array(2147483648, -2147483648, -1, 4294901761);
+ foreach($badvals as $badval) {
+ if(ut_nfmt_get_symbol( $fmt, 2147483648 )) {
+ $res_str .= "Bad value $badval should return false!\n";
+ }
+ }
+ 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: 12,345.123
+
+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 100644
index 0000000..539ff52
--- /dev/null
+++ b/ext/intl/tests/formatter_get_set_text_attribute.phpt
@@ -0,0 +1,121 @@
+--TEST--
+numfmt_get/set_text_attribute()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set text attribute.
+ */
+
+
+function ut_main()
+{
+ // Array with data for testing
+ $long_str = str_repeat('blah', 100);
+ $attributes = array(
+ 'POSITIVE_PREFIX' => array( NumberFormatter::POSITIVE_PREFIX, '_+_', 12345.1234 ),
+ 'POSITIVE_SUFFIX' => array( NumberFormatter::POSITIVE_SUFFIX, '_+_', 12345.1234 ),
+ 'NEGATIVE_PREFIX' => array( NumberFormatter::NEGATIVE_PREFIX, '_-_', -12345.1234 ),
+ 'NEGATIVE_SUFFIX' => array( NumberFormatter::NEGATIVE_SUFFIX, '_-_', -12345.1234 ),
+ 'PADDING_CHARACTER' => array( NumberFormatter::PADDING_CHARACTER, '^', 12345.1234 ),
+ 'POSITIVE_PREFIX-2' => array( NumberFormatter::POSITIVE_PREFIX, $long_str, 12345.1234 ),
+// 'CURRENCY_CODE' => array( NumberFormatter::CURRENCY_CODE, '_C_', 12345.1234 )
+// 'DEFAULT_RULESET' => array( NumberFormatter::DEFAULT_RULESET, '_DR_', 12345.1234 ),
+// 'PUBLIC_RULESETS' => array( NumberFormatter::PUBLIC_RULESETS, '_PR_', 12345.1234 )
+ );
+
+ $res_str = '';
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+
+ foreach( $attributes as $attr_name => $data )
+ {
+ list( $attr, $new_val, $test_number ) = $data;
+ $res_str .= "\nAttribute $attr_name\n";
+
+ if( $attr == NumberFormatter::PADDING_CHARACTER )
+ ut_nfmt_set_attribute( $fmt, NumberFormatter::FORMAT_WIDTH, 21 );
+
+ // Get default attribute's value
+ $def_val = ut_nfmt_get_text_attribute( $fmt, $attr );
+ if( $def_val === false )
+ $res_str .= "get_text_attribute() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ $res_str .= "Default value: [$def_val]\n";
+ $res_str .= "Formatting number with default value: " . ut_nfmt_format( $fmt, $test_number ) . "\n";
+
+ // Set new attribute's value and see if it works out.
+ $res_val = ut_nfmt_set_text_attribute( $fmt, $attr, $new_val );
+ if( !$res_val )
+ $res_str .= "set_text_attribute() error: " . ut_nfmt_get_error_message( $fmt ) . "\n";
+
+ // Get attribute value back.
+ $new_val_check = ut_nfmt_get_text_attribute( $fmt, $attr );
+ $res_str .= "New value: [$new_val_check]\n";
+ $res_str .= "Formatting number with new value: " . ut_nfmt_format( $fmt, $test_number ) . "\n";
+
+ // Check if the new value has been set.
+ if( $new_val !== $new_val_check )
+ $res_str .= "ERROR: New $attr_name symbol value has not been set correctly.\n";
+
+ // Restore attribute's value to default
+ ut_nfmt_set_text_attribute( $fmt, $attr, $def_val );
+
+ if( $attr == NumberFormatter::PADDING_CHARACTER )
+ ut_nfmt_set_attribute( $fmt, NumberFormatter::FORMAT_WIDTH, 0 );
+ }
+
+ //
+ $fmt = ut_nfmt_create( "uk_UA", NumberFormatter::CURRENCY );
+ $res_str .= sprintf( "\nCurrency ISO-code for locale 'uk_UA' is: %s\n",
+ ut_nfmt_get_text_attribute( $fmt, NumberFormatter::CURRENCY_CODE ) );
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Attribute POSITIVE_PREFIX
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [_+_]
+Formatting number with new value: _+_12,345.123
+
+Attribute POSITIVE_SUFFIX
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [_+_]
+Formatting number with new value: 12,345.123_+_
+
+Attribute NEGATIVE_PREFIX
+Default value: [-]
+Formatting number with default value: -12,345.123
+New value: [_-_]
+Formatting number with new value: _-_12,345.123
+
+Attribute NEGATIVE_SUFFIX
+Default value: []
+Formatting number with default value: -12,345.123
+New value: [_-_]
+Formatting number with new value: -12,345.123_-_
+
+Attribute PADDING_CHARACTER
+Default value: [*]
+Formatting number with default value: ***********12,345.123
+New value: [^]
+Formatting number with new value: ^^^^^^^^^^^12,345.123
+
+Attribute POSITIVE_PREFIX-2
+Default value: []
+Formatting number with default value: 12,345.123
+New value: [blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah]
+Formatting number with new value: blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah12,345.123
+
+Currency ISO-code for locale 'uk_UA' is: UAH
+
+
diff --git a/ext/intl/tests/formatter_parse.phpt b/ext/intl/tests/formatter_parse.phpt
new file mode 100644
index 0000000..2ab9cce
--- /dev/null
+++ b/ext/intl/tests/formatter_parse.phpt
@@ -0,0 +1,42 @@
+--TEST--
+numfmt_parse()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Number parsing.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Test parsing float number.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $res_str .= ut_nfmt_parse( $fmt, "123E-3" ) . "\n";
+
+ // Test parsing float number as integer.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $res_str .= ut_nfmt_parse( $fmt, "1.23", NumberFormatter::TYPE_INT32 ) . "\n";
+
+ // Test specifying non-zero parsing start position.
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::DECIMAL );
+ $pos = 2;
+ $res_str .= ut_nfmt_parse( $fmt, "0.123 here", NumberFormatter::TYPE_DOUBLE, $pos ) . "\n";
+ $res_str .= "$pos\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+0.123
+1
+123
+5
diff --git a/ext/intl/tests/formatter_parse_currency.phpt b/ext/intl/tests/formatter_parse_currency.phpt
new file mode 100644
index 0000000..4ebd1a4
--- /dev/null
+++ b/ext/intl/tests/formatter_parse_currency.phpt
@@ -0,0 +1,37 @@
+--TEST--
+numfmt_parse_currency()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Currency parsing.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $pos = 0;
+ $currency = '';
+ $num = ut_nfmt_parse_currency( $fmt, '$9,988,776.65', $currency, $pos );
+ $res_str .= "$num $currency\n";
+
+ $fmt = ut_nfmt_create( "en_US", NumberFormatter::CURRENCY );
+ $pos = 1;
+ $currency = '';
+ $num = ut_nfmt_parse_currency( $fmt, ' $123.45', $currency, $pos );
+ $res_str .= "$num $currency\n";
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+9988776.65 USD
+123.45 USD
diff --git a/ext/intl/tests/grapheme.phpt b/ext/intl/tests/grapheme.phpt
new file mode 100644
index 0000000..def9110
--- /dev/null
+++ b/ext/intl/tests/grapheme.phpt
@@ -0,0 +1,1192 @@
+--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( "ababc", 0, 10, "ababc" ),
+
+ array( "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq", 0, 10 , "a" . $char_a_ring_nfd . "bc" . $char_o_diaeresis_nfd . "Opq" ),
+ 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( "http://news.bbc.co.uk/2/hi/middle_east/7831588.stm", 48, 48 , 50 , "tm" ),
+
+ 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 3
+"ab" + "hindi" + "cde" strlen 8
+"" 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 "ababc" from "0" - grapheme_substr with length 10 = ababc == ababc
+substring of "aa%CC%8Abco%CC%88Opq" from "0" - grapheme_substr with length 10 = aa%CC%8Abco%CC%88Opq == aa%CC%8Abco%CC%88Opq
+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 "http%3A%2F%2Fnews.bbc.co.uk%2F2%2Fhi%2Fmiddle_east%2F7831588.stm" "48" graphemes - grapheme_extract starting at byte position 48 with $next = tm == tm $next=50 == 50
+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/idn.phpt b/ext/intl/tests/idn.phpt
new file mode 100644
index 0000000..fde822a
--- /dev/null
+++ b/ext/intl/tests/idn.phpt
@@ -0,0 +1,18 @@
+--TEST--
+IDN
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Test IDN functions (procedural only)
+ */
+
+echo idn_to_ascii("t\xC3\xA4st.de")."\n";
+echo urlencode(idn_to_utf8('xn--tst-qla.de'))."\n";
+
+?>
+--EXPECT--
+xn--tst-qla.de
+t%C3%A4st.de \ No newline at end of file
diff --git a/ext/intl/tests/idn_uts46_basic.phpt b/ext/intl/tests/idn_uts46_basic.phpt
new file mode 100644
index 0000000..2ca1850
--- /dev/null
+++ b/ext/intl/tests/idn_uts46_basic.phpt
@@ -0,0 +1,53 @@
+--TEST--
+IDN UTS #46 API basic tests
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl'))
+ die('skip');
+ if (!defined('INTL_IDNA_VARIANT_UTS46'))
+ die('skip no UTS #46 API');
+--FILE--
+<?php
+$utf8dn = "www.fußball.com";
+$asciiNonTrans = "www.xn--fuball-cta.com";
+
+echo "all ok, no details:", "\n";
+var_dump(idn_to_ascii($utf8dn,
+ IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46));
+
+echo "all ok, no details, transitional:", "\n";
+var_dump(idn_to_ascii($utf8dn, 0, INTL_IDNA_VARIANT_UTS46));
+
+echo "all ok, with details:", "\n";
+var_dump(idn_to_ascii($utf8dn, IDNA_NONTRANSITIONAL_TO_ASCII,
+ INTL_IDNA_VARIANT_UTS46, $info));
+var_dump($info);
+
+echo "reverse, ok, with details:", "\n";
+var_dump(idn_to_utf8($asciiNonTrans, 0, INTL_IDNA_VARIANT_UTS46, $info));
+var_dump($info);
+--EXPECT--
+all ok, no details:
+string(22) "www.xn--fuball-cta.com"
+all ok, no details, transitional:
+string(16) "www.fussball.com"
+all ok, with details:
+string(22) "www.xn--fuball-cta.com"
+array(3) {
+ ["result"]=>
+ string(22) "www.xn--fuball-cta.com"
+ ["isTransitionalDifferent"]=>
+ bool(true)
+ ["errors"]=>
+ int(0)
+}
+reverse, ok, with details:
+string(16) "www.fußball.com"
+array(3) {
+ ["result"]=>
+ string(16) "www.fußball.com"
+ ["isTransitionalDifferent"]=>
+ bool(false)
+ ["errors"]=>
+ int(0)
+}
diff --git a/ext/intl/tests/idn_uts46_errors.phpt b/ext/intl/tests/idn_uts46_errors.phpt
new file mode 100644
index 0000000..a336e69
--- /dev/null
+++ b/ext/intl/tests/idn_uts46_errors.phpt
@@ -0,0 +1,89 @@
+--TEST--
+IDN UTS #46 API error tests
+--SKIPIF--
+<?php
+ if (!extension_loaded('intl'))
+ die('skip');
+ if (!defined('INTL_IDNA_VARIANT_UTS46'))
+ die('skip no UTS #46 API');
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+echo "=> PHP level errors", "\n";
+
+echo "bad args:", "\n";
+var_dump(idn_to_ascii("", 0, array()));
+var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46, $foo, null));
+
+echo "bad variant:", "\n";
+var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46 + 10));
+
+echo "empty domain:", "\n";
+var_dump(idn_to_ascii("", 0, INTL_IDNA_VARIANT_UTS46));
+
+echo "fourth arg for 2003 variant (only notice raised):", "\n";
+var_dump(idn_to_ascii("foo.com", 0, INTL_IDNA_VARIANT_2003, $foo));
+
+echo "with error, but no details arg:", "\n";
+var_dump(idn_to_ascii("www.fußball.com-", 0, INTL_IDNA_VARIANT_UTS46));
+
+echo "with error, with details arg:", "\n";
+var_dump(idn_to_ascii("www.fußball.com-", IDNA_NONTRANSITIONAL_TO_ASCII,
+ INTL_IDNA_VARIANT_UTS46, $foo));
+var_dump($foo);
+
+echo "with error, with details arg, contextj:", "\n";
+var_dump(idn_to_ascii(
+ html_entity_decode("www.a&#x200D;b.com", 0, "UTF-8"),
+ IDNA_NONTRANSITIONAL_TO_ASCII | IDNA_CHECK_CONTEXTJ,
+ INTL_IDNA_VARIANT_UTS46, $foo));
+var_dump($foo);
+var_dump($foo["errors"]==IDNA_ERROR_CONTEXTJ);
+--EXPECTF--
+=> PHP level errors
+bad args:
+
+Warning: idn_to_ascii() expects parameter 3 to be long, array given in %s on line %d
+
+Warning: idn_to_ascii(): idn_to_ascii: bad arguments in %s on line %d
+NULL
+
+Warning: idn_to_ascii() expects at most 4 parameters, 5 given in %s on line %d
+
+Warning: idn_to_ascii(): idn_to_ascii: bad arguments in %s on line %d
+NULL
+bad variant:
+
+Warning: idn_to_ascii(): idn_to_ascii: invalid variant, must be one of {INTL_IDNA_VARIANT_2003, INTL_IDNA_VARIANT_UTS46} in %s on line %d
+bool(false)
+empty domain:
+
+Warning: idn_to_ascii(): idn_to_ascii: empty domain name in %s on line %d
+bool(false)
+fourth arg for 2003 variant (only notice raised):
+
+Notice: idn_to_ascii(): 4 arguments were provided, but INTL_IDNA_VARIANT_2003 only takes 3 - extra argument ignored in %s on line %d
+string(7) "foo.com"
+with error, but no details arg:
+bool(false)
+with error, with details arg:
+bool(false)
+array(3) {
+ ["result"]=>
+ string(23) "www.xn--fuball-cta.com-"
+ ["isTransitionalDifferent"]=>
+ bool(true)
+ ["errors"]=>
+ int(16)
+}
+with error, with details arg, contextj:
+bool(false)
+array(3) {
+ ["result"]=>
+ string(18) "www.xn--ab-m1t.com"
+ ["isTransitionalDifferent"]=>
+ bool(true)
+ ["errors"]=>
+ int(4096)
+}
+bool(true)
diff --git a/ext/intl/tests/intl_error_name.phpt b/ext/intl/tests/intl_error_name.phpt
new file mode 100644
index 0000000..8f5b2dc
--- /dev/null
+++ b/ext/intl/tests/intl_error_name.phpt
@@ -0,0 +1,25 @@
+--TEST--
+intl_error_name()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Check getting error string by integer error code.
+ */
+
+
+function check( $err_code )
+{
+ echo intl_error_name( $err_code ) . "\n";
+}
+
+check( U_ZERO_ERROR );
+check( U_ILLEGAL_ARGUMENT_ERROR );
+check( U_USING_FALLBACK_WARNING );
+?>
+--EXPECT--
+U_ZERO_ERROR
+U_ILLEGAL_ARGUMENT_ERROR
+U_USING_FALLBACK_WARNING
diff --git a/ext/intl/tests/intl_get_error_code.phpt b/ext/intl/tests/intl_get_error_code.phpt
new file mode 100644
index 0000000..6cd361b
--- /dev/null
+++ b/ext/intl/tests/intl_get_error_code.phpt
@@ -0,0 +1,24 @@
+--TEST--
+intl_get_error_code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check getting global error code.
+ */
+
+// Suppress warning messages.
+error_reporting( E_ERROR );
+
+if( collator_get_locale() !== false )
+ echo "failed\n";
+else
+{
+ $check_code = ( intl_get_error_code() != 0 );
+ echo ( $check_code ? "ok" : "failed" ) . "\n";
+}
+
+?>
+--EXPECT--
+ok
diff --git a/ext/intl/tests/intl_get_error_message.phpt b/ext/intl/tests/intl_get_error_message.phpt
new file mode 100644
index 0000000..f81b5c0
--- /dev/null
+++ b/ext/intl/tests/intl_get_error_message.phpt
@@ -0,0 +1,21 @@
+--TEST--
+intl_get_error_message()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check getting global error message.
+ */
+
+// Suppress warning messages.
+error_reporting( E_ERROR );
+
+if( collator_get_locale() !== false )
+ echo "failed\n";
+else
+ printf( "%s\n", intl_get_error_message() );
+
+?>
+--EXPECT--
+collator_get_locale: unable to parse input params: U_ILLEGAL_ARGUMENT_ERROR
diff --git a/ext/intl/tests/intl_icu_data_version_constant.phpt b/ext/intl/tests/intl_icu_data_version_constant.phpt
new file mode 100644
index 0000000..ad01218
--- /dev/null
+++ b/ext/intl/tests/intl_icu_data_version_constant.phpt
@@ -0,0 +1,10 @@
+--TEST--
+INTL_ICU_DATA_VERSION constant
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) || version_compare('INTL_ICU_VERSION', '4.4', '<') ) print 'skip'; ?>
+--FILE--
+<?php
+var_dump(defined("INTL_ICU_DATA_VERSION"));
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/intl/tests/intl_icu_version_constant.phpt b/ext/intl/tests/intl_icu_version_constant.phpt
new file mode 100644
index 0000000..1316069
--- /dev/null
+++ b/ext/intl/tests/intl_icu_version_constant.phpt
@@ -0,0 +1,10 @@
+--TEST--
+INTL_ICU_VERSION constant
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+var_dump(defined("INTL_ICU_VERSION"));
+?>
+--EXPECT--
+bool(true)
diff --git a/ext/intl/tests/intl_is_failure.phpt b/ext/intl/tests/intl_is_failure.phpt
new file mode 100644
index 0000000..e07df8f
--- /dev/null
+++ b/ext/intl/tests/intl_is_failure.phpt
@@ -0,0 +1,25 @@
+--TEST--
+intl_is_failure()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check determining failure error codes.
+ */
+
+
+function check( $err_code )
+{
+ var_export( intl_is_failure( $err_code ) );
+ echo "\n";
+}
+
+check( U_ZERO_ERROR );
+check( U_USING_FALLBACK_WARNING );
+check( U_ILLEGAL_ARGUMENT_ERROR );
+?>
+--EXPECT--
+false
+false
+true
diff --git a/ext/intl/tests/locale_accept.phpt b/ext/intl/tests/locale_accept.phpt
new file mode 100644
index 0000000..cf1dc6e
--- /dev/null
+++ b/ext/intl/tests/locale_accept.phpt
@@ -0,0 +1,46 @@
+--TEST--
+locale_accept_from_http
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = "";
+ $http_acc = array(
+ 'en-us,en;q=0.5',
+ 'da, en-gb;q=0.8, en;q=0.7',
+ 'zh, en-us;q=0.8, en;q=0.7',
+ 'xx, fr-FR;q=0.3, de-DE;q=0.5',
+ 'none',
+ array()
+ );
+
+ foreach($http_acc as $http) {
+ $res = ut_loc_accept_http($http);
+ $res_str .= @"Accepting $http: $res\n";
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTF--
+Warning: Locale::acceptFromHttp() expects parameter 1 to be string, array given in %s on line %d
+
+Warning: locale_accept_from_http() expects parameter 1 to be string, array given in %s on line %d
+Accepting en-us,en;q=0.5: en_US
+Accepting da, en-gb;q=0.8, en;q=0.7: da
+Accepting zh, en-us;q=0.8, en;q=0.7: zh
+Accepting xx, fr-FR;q=0.3, de-DE;q=0.5: de_DE
+Accepting none:
+Accepting Array:
diff --git a/ext/intl/tests/locale_compose_locale.phpt b/ext/intl/tests/locale_compose_locale.phpt
new file mode 100644
index 0000000..2d0ca54
--- /dev/null
+++ b/ext/intl/tests/locale_compose_locale.phpt
@@ -0,0 +1,189 @@
+--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()
+{
+ $loc_parts_arr1 = array(
+ Locale::LANG_TAG =>'sl' ,
+ Locale::SCRIPT_TAG =>'Latn' ,
+ Locale::REGION_TAG =>'IT'
+ );
+ $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 => 45,
+ Locale::REGION_TAG => false,
+ Locale::SCRIPT_TAG => 15
+ );
+ $loc_parts_arr11 = array(
+ Locale::LANG_TAG =>'de' ,
+ Locale::REGION_TAG =>'DE',
+ 'private0' => 13,
+ 'variant1' => array(),
+ 'extlang2' => false
+ );
+ $loc_parts_arr12 = array(
+ Locale::LANG_TAG =>'en' ,
+ Locale::SCRIPT_TAG =>'Hans' ,
+ Locale::REGION_TAG =>'CN',
+ Locale::VARIANT_TAG => array('nedis', 'rozaj'),
+ Locale::PRIVATE_TAG => array('prv1', 'prv2'),
+ Locale::EXTLANG_TAG => array('lng', 'ing')
+ );
+
+
+ $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
+ );
+
+ 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: sl_Latn_IT
+------------
+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:
+locale_compose: parameter array element is not a string: U_ILLEGAL_ARGUMENT_ERROR
+------------
+Input Array name is : loc11
+
+Composed Locale: No values found from Locale compose due to the following error:
+locale_compose: parameter array element is not a string: U_ILLEGAL_ARGUMENT_ERROR
+------------
+Input Array name is : loc12
+
+Composed Locale: en_lng_ing_Hans_CN_nedis_rozaj_x_prv1_prv2
+------------
diff --git a/ext/intl/tests/locale_filter_matches.phpt b/ext/intl/tests/locale_filter_matches.phpt
new file mode 100644
index 0000000..cada499
--- /dev/null
+++ b/ext/intl/tests/locale_filter_matches.phpt
@@ -0,0 +1,366 @@
+--TEST--
+locale_filter_matches.phpt() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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_filter_matches2.phpt b/ext/intl/tests/locale_filter_matches2.phpt
new file mode 100644
index 0000000..37f9e5a
--- /dev/null
+++ b/ext/intl/tests/locale_filter_matches2.phpt
@@ -0,0 +1,366 @@
+--TEST--
+locale_filter_matches.phpt() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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 @x=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 lb ? 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 @x=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 lb ? 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 @x=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 lb ? 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 @x=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 lb ? 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 @x=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 lb ? 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 @x=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 lb ? 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 100644
index 0000000..864b8a2
--- /dev/null
+++ b/ext/intl/tests/locale_get_all_variants.phpt
@@ -0,0 +1,62 @@
+--TEST--
+locale_get_all_variants.phpt()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'sl_IT_nedis_KIRTI',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'zh-hakka',
+ 'zh-wuu',
+ 'i-tay',
+ 'sgn-BE-nl',
+ 'sgn-CH-de',
+ 'sl_IT_rozaj@currency=EUR'
+ );
+ $res_str = '';
+ foreach($locales as $locale){
+ $variants_arr = ut_loc_locale_get_all_variants( $locale);
+ $res_str .= "$locale : variants ";
+ if( $variants_arr){
+ foreach($variants_arr as $variant){
+ $res_str .= "'$variant',";
+ }
+ }else{
+ $res_str .= "--none";
+ }
+ $res_str .= "\n";
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+sl_IT_nedis_KIRTI : variants 'NEDIS','KIRTI',
+sl_IT_nedis-a-kirti-x-xyz : variants 'NEDIS',
+sl_IT_rozaj : variants 'ROZAJ',
+sl_IT_NEDIS_ROJAZ_1901 : variants 'NEDIS','ROJAZ','1901',
+i-enochian : variants --none
+zh-hakka : variants --none
+zh-wuu : variants --none
+i-tay : variants --none
+sgn-BE-nl : variants --none
+sgn-CH-de : variants --none
+sl_IT_rozaj@currency=EUR : variants 'ROZAJ',
diff --git a/ext/intl/tests/locale_get_default.phpt b/ext/intl/tests/locale_get_default.phpt
new file mode 100644
index 0000000..57c46cc
--- /dev/null
+++ b/ext/intl/tests/locale_get_default.phpt
@@ -0,0 +1,46 @@
+--TEST--
+locale_get_default()
+--INI--
+intl.default_locale=en-US
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the default Locale with different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ locale_set_default('de-DE');
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ ini_set('intl.default_locale', 'fr');
+ $lang = ut_loc_get_default() ;
+ $res_str .= "Default locale: $lang";
+ $res_str .= "\n";
+
+ ini_restore("intl.default_locale");
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+Default locale: en-US
+Default locale: de-DE
+Default locale: fr
diff --git a/ext/intl/tests/locale_get_display_language.phpt b/ext/intl/tests/locale_get_display_language.phpt
new file mode 100644
index 0000000..2e00056
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_language.phpt
@@ -0,0 +1,280 @@
+--TEST--
+locale_get_display_language()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_language for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str='';
+
+ $disp_locales=array('en','fr','de');
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+ 'art-lojban', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $res_str .= "locale='$locale'\n";
+ foreach( $disp_locales as $disp_locale )
+ {
+ $scr = ut_loc_get_display_language( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_language=$scr";
+ $res_str .= "\n";
+ }
+ $res_str .= "-----------------\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTREGEX--
+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=[rR]oot
+-----------------
+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 100644
index 0000000..b128734
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_name.phpt
@@ -0,0 +1,342 @@
+--TEST--
+locale_get_display_name() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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 );
+ $scr = str_replace(array('(', ')'), '#', $scr);
+ $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();
+
+?>
+--EXPECTREGEX--
+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|Resianisch)#
+-----------------
+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, [Cc]urrency=Euro#
+disp_locale=fr : display_name=slovène #Italie, dialecte de Resia, Devise=euro#
+disp_locale=de : display_name=Slowenisch #Italien, (ROZAJ|Resianisch), Währung=Euro#
+-----------------
+locale='uk-ua_CALIFORNIA@currency=;currency=GRN'
+disp_locale=en : display_name=Ukrainian #Ukraine, CALIFORNIA, [Cc]urrency#
+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=[Rr]oot
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_name=Ukrainian #[Cc]urrency=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#|traditionnels)#
+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#|simplifiés)#
+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#|simplifiés), Chine#
+disp_locale=de : display_name=Chinesisch #Vereinfachte Chinesische Schrift, China#
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_name=Serbian #Latin, Serbia [aA]nd 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|Resianisch)#)?
+-----------------
+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|[aA]lte deutsche Rechtschreibung)#
+-----------------
+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|Natisone-Dialekt)#
+-----------------
+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|Natisone-Dialekt)#
+-----------------
+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 [aA]nd 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_name2.phpt b/ext/intl/tests/locale_get_display_name2.phpt
new file mode 100644
index 0000000..40ccc0c
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_name2.phpt
@@ -0,0 +1,342 @@
+--TEST--
+locale_get_display_name() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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 );
+ $scr = str_replace(array('(', ')'), '#', $scr);
+ $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();
+
+?>
+--EXPECTREGEX--
+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|Resianisch)#
+-----------------
+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 #Private-Use=i-enochian#
+disp_locale=fr : display_name=i-enochian #Usage privé=i-enochian#
+disp_locale=de : display_name=i-enochian #Privatnutzung=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, [Cc]urrency=Euro#
+disp_locale=fr : display_name=slovène #Italie, dialecte de Resia, Devise=euro#
+disp_locale=de : display_name=Slowenisch #Italien, (ROZAJ|Resianisch), Währung=Euro#
+-----------------
+locale='uk-ua_CALIFORNIA@currency=;currency=GRN'
+disp_locale=en : display_name=Ukrainian #Ukraine, CALIFORNIA, [Cc]urrency#
+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=[Rr]oot
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_name=Ukrainian #[Cc]urrency=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 #Private-Use=i-enochian#
+disp_locale=fr : display_name=i-enochian #Usage privé=i-enochian#
+disp_locale=de : display_name=i-enochian #Privatnutzung=i-enochian#
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_name=Chinese #Traditional#
+disp_locale=fr : display_name=chinois #idéogrammes han (#variante traditionnelle#|traditionnels)#
+disp_locale=de : display_name=Chinesisch #Traditionell#
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_name=Chinese #Simplified#
+disp_locale=fr : display_name=chinois #idéogrammes han (#variante simplifiée#|simplifiés)#
+disp_locale=de : display_name=Chinesisch #Vereinfacht#
+-----------------
+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, China#
+disp_locale=fr : display_name=chinois #idéogrammes han (#variante simplifiée#|simplifiés), Chine#
+disp_locale=de : display_name=Chinesisch #Vereinfacht, China#
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_name=Serbian #Latin, Serbia [aA]nd 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 #Resian#
+disp_locale=fr : display_name=slovène #dialecte de Resia#
+disp_locale=de : display_name=Slowenisch( #(ROZAJ|Resianisch)#)?
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_name=Slovenian #Natisone dialect#
+disp_locale=fr : display_name=slovène #dialecte de Natisone#
+disp_locale=de : display_name=Slowenisch #Natisone-Dialekt#
+-----------------
+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|[aA]lte deutsche Rechtschreibung)#
+-----------------
+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|Natisone-Dialekt)#
+-----------------
+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|Natisone-Dialekt)#
+-----------------
+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#
+disp_locale=fr : display_name=espagnol #Amérique latine#
+disp_locale=de : display_name=Spanisch #Lateinamerika#
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_name=German #Switzerland, Private-Use=phonebk#
+disp_locale=fr : display_name=allemand #Suisse, Usage privé=phonebk#
+disp_locale=de : display_name=Deutsch #Schweiz, Privatnutzung=phonebk#
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_name=Azerbaijani #Arabic, Private-Use=aze-derbend#
+disp_locale=fr : display_name=azéri #arabe, Usage privé=aze-derbend#
+disp_locale=de : display_name=Aserbaidschanisch #Arabisch, Privatnutzung=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 #Private-Use=whatever#
+disp_locale=fr : display_name=x-whatever #Usage privé=whatever#
+disp_locale=de : display_name=x-whatever #Privatnutzung=whatever#
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_name=qaa #Qaaa, QM, Private-Use=southern#
+disp_locale=fr : display_name=qaa #Qaaa, QM, Usage privé=southern#
+disp_locale=de : display_name=qaa #Qaaa, QM, Privatnutzung=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 [aA]nd 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, attribute=islamcal#
+disp_locale=fr : display_name=anglais #États-Unis, attribute=islamcal#
+disp_locale=de : display_name=Englisch #Vereinigte Staaten, attribute=islamcal#
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_name=Chinese #China, a=myext, Private-Use=private#
+disp_locale=fr : display_name=chinois #Chine, a=myext, Usage privé=private#
+disp_locale=de : display_name=Chinesisch #China, a=myext, Privatnutzung=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, DE#
+disp_locale=fr : display_name=allemand #Amérique latine, DE#
+disp_locale=de : display_name=Deutsch #Lateinamerika, 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#
+disp_locale=fr : display_name=arabe #a=aaa, b=bbb#
+disp_locale=de : display_name=Arabisch #a=aaa, b=bbb#
+-----------------
diff --git a/ext/intl/tests/locale_get_display_region.phpt b/ext/intl/tests/locale_get_display_region.phpt
new file mode 100644
index 0000000..9a6e75d
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_region.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_region() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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();
+
+?>
+--EXPECTREGEX--
+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 [Aa]nd 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 [Aa]nd 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_region2.phpt b/ext/intl/tests/locale_get_display_region2.phpt
new file mode 100644
index 0000000..f1b5841
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_region2.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_region() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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();
+
+?>
+--EXPECTREGEX--
+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 [Aa]nd 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
+disp_locale=fr : display_region=Amérique latine
+disp_locale=de : display_region=Lateinamerika
+-----------------
+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 [Aa]nd 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
+disp_locale=fr : display_region=Amérique latine
+disp_locale=de : display_region=Lateinamerika
+-----------------
+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 100644
index 0000000..0440dfb
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_script.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_script() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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();
+
+?>
+--EXPECTREGEX--
+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\)|traditionnels)
+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\)|simplifiés)
+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\)|simplifiés)
+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_script2.phpt b/ext/intl/tests/locale_get_display_script2.phpt
new file mode 100644
index 0000000..2b9e037
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_script2.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_script() icu = 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0 || version_compare(INTL_ICU_VERSION, '49') >= 0) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_script for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $disp_locales=array('en','fr','de');
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $res_str .= "locale='$locale'\n";
+ foreach( $disp_locales as $disp_locale )
+ {
+ $scr = ut_loc_get_display_script( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_script=$scr";
+ $res_str .= "\n";
+ }
+ $res_str .= "-----------------\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTREGEX--
+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
+disp_locale=fr : display_script=idéogrammes han (\(variante traditionnelle\)|traditionnels)
+disp_locale=de : display_script=Traditionell
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_script=Simplified
+disp_locale=fr : display_script=idéogrammes han (\(variante simplifiée\)|simplifiés)
+disp_locale=de : display_script=Vereinfacht
+-----------------
+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
+disp_locale=fr : display_script=idéogrammes han (\(variante simplifiée\)|simplifiés)
+disp_locale=de : display_script=Vereinfacht
+-----------------
+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_script3.phpt b/ext/intl/tests/locale_get_display_script3.phpt
new file mode 100644
index 0000000..447766e
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_script3.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_script() icu >= 49
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '49') < 0) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the display_script for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $disp_locales=array('en','fr','de');
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $res_str .= "locale='$locale'\n";
+ foreach( $disp_locales as $disp_locale )
+ {
+ $scr = ut_loc_get_display_script( $locale ,$disp_locale );
+ $res_str .= "disp_locale=$disp_locale : display_script=$scr";
+ $res_str .= "\n";
+ }
+ $res_str .= "-----------------\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+locale='uk-ua_CALIFORNIA@currency=;currency=GRN'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='root'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='uk@currency=EURO'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='Hindi'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='de'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='fr'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='ja'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='i-enochian'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='zh-Hant'
+disp_locale=en : display_script=Traditional Han
+disp_locale=fr : display_script=chinois traditionnel
+disp_locale=de : display_script=Traditionelles Chinesisch
+-----------------
+locale='zh-Hans'
+disp_locale=en : display_script=Simplified Han
+disp_locale=fr : display_script=chinois simplifié
+disp_locale=de : display_script=Vereinfachtes Chinesisch
+-----------------
+locale='sr-Cyrl'
+disp_locale=en : display_script=Cyrillic
+disp_locale=fr : display_script=cyrillique
+disp_locale=de : display_script=Kyrillisch
+-----------------
+locale='sr-Latn'
+disp_locale=en : display_script=Latin
+disp_locale=fr : display_script=latin
+disp_locale=de : display_script=Lateinisch
+-----------------
+locale='zh-Hans-CN'
+disp_locale=en : display_script=Simplified Han
+disp_locale=fr : display_script=chinois simplifié
+disp_locale=de : display_script=Vereinfachtes Chinesisch
+-----------------
+locale='sr-Latn-CS'
+disp_locale=en : display_script=Latin
+disp_locale=fr : display_script=latin
+disp_locale=de : display_script=Lateinisch
+-----------------
+locale='sl-rozaj'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='de-CH-1901'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='sl-IT-nedis'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='sl-Latn-IT-nedis'
+disp_locale=en : display_script=Latin
+disp_locale=fr : display_script=latin
+disp_locale=de : display_script=Lateinisch
+-----------------
+locale='de-DE'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='en-US'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='es-419'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='de-CH-x-phonebk'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='az-Arab-x-AZE-derbend'
+disp_locale=en : display_script=Arabic
+disp_locale=fr : display_script=arabe
+disp_locale=de : display_script=Arabisch
+-----------------
+locale='zh-min'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='zh-min-nan-Hant-CN'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='x-whatever'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='qaa-Qaaa-QM-x-southern'
+disp_locale=en : display_script=Qaaa
+disp_locale=fr : display_script=Qaaa
+disp_locale=de : display_script=Qaaa
+-----------------
+locale='sr-Latn-QM'
+disp_locale=en : display_script=Latin
+disp_locale=fr : display_script=latin
+disp_locale=de : display_script=Lateinisch
+-----------------
+locale='sr-Qaaa-CS'
+disp_locale=en : display_script=Qaaa
+disp_locale=fr : display_script=Qaaa
+disp_locale=de : display_script=Qaaa
+-----------------
+locale='en-US-u-islamCal'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='en-a-myExt-b-another'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='de-419-DE'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='a-DE'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
+locale='ar-a-aaa-b-bbb-a-ccc'
+disp_locale=en : display_script=
+disp_locale=fr : display_script=
+disp_locale=de : display_script=
+-----------------
diff --git a/ext/intl/tests/locale_get_display_variant.phpt b/ext/intl/tests/locale_get_display_variant.phpt
new file mode 100644
index 0000000..9f72ee0
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_variant.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_variant() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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();
+
+?>
+--EXPECTREGEX--
+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|[Aa]lte deutsche Rechtschreibung)
+-----------------
+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|Natisone-Dialekt)
+-----------------
+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|Natisone-Dialekt)
+-----------------
+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_display_variant2.phpt b/ext/intl/tests/locale_get_display_variant2.phpt
new file mode 100644
index 0000000..2517240
--- /dev/null
+++ b/ext/intl/tests/locale_get_display_variant2.phpt
@@ -0,0 +1,275 @@
+--TEST--
+locale_get_display_variant() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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();
+
+?>
+--EXPECTREGEX--
+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=Resian
+disp_locale=fr : display_variant=dialecte de Resia
+disp_locale=de : display_variant=Resianisch
+-----------------
+locale='sl-nedis'
+disp_locale=en : display_variant=Natisone dialect
+disp_locale=fr : display_variant=dialecte de Natisone
+disp_locale=de : display_variant=Natisone-Dialekt
+-----------------
+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|[Aa]lte deutsche Rechtschreibung)
+-----------------
+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|Natisone-Dialekt)
+-----------------
+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|Natisone-Dialekt)
+-----------------
+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=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+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=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+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=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+locale='zh-CN-a-myExt-x-private'
+disp_locale=en : display_variant=
+disp_locale=fr : display_variant=
+disp_locale=de : display_variant=
+-----------------
+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 100644
index 0000000..4a09f54
--- /dev/null
+++ b/ext/intl/tests/locale_get_keywords.phpt
@@ -0,0 +1,140 @@
+--TEST--
+locale_get_keywords() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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_keywords2.phpt b/ext/intl/tests/locale_get_keywords2.phpt
new file mode 100644
index 0000000..7bcaca1
--- /dev/null
+++ b/ext/intl/tests/locale_get_keywords2.phpt
@@ -0,0 +1,151 @@
+--TEST--
+locale_get_keywords() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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: Key is x and Value is i-enochian
+
+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: Key is x and Value is phonebk
+
+az-Arab-x-AZE-derbend: Key is x and Value is aze-derbend
+
+zh-min: No keywords found.
+zh-min-nan-Hant-CN: No keywords found.
+x-whatever: Key is x and Value is whatever
+
+qaa-Qaaa-QM-x-southern: Key is x and Value is southern
+
+sr-Latn-QM: No keywords found.
+sr-Qaaa-CS: No keywords found.
+en-US-u-islamCal: Key is attribute and Value is islamcal
+
+zh-CN-a-myExt-x-private: Key is a and Value is myext
+Key is x and Value is private
+
+en-a-myExt-b-another: Key is a and Value is myext
+Key is b and Value is another
+
+de-419-DE: No keywords found.
+a-DE: No keywords found.
+ar-a-aaa-b-bbb-a-ccc: Key is a and Value is aaa
+Key is b and Value is bbb
diff --git a/ext/intl/tests/locale_get_primary_language.phpt b/ext/intl/tests/locale_get_primary_language.phpt
new file mode 100644
index 0000000..bc92e92
--- /dev/null
+++ b/ext/intl/tests/locale_get_primary_language.phpt
@@ -0,0 +1,121 @@
+--TEST--
+locale_get_primary_language()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the prmary language for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $lang = ut_loc_get_primary_language( $locale);
+ $res_str .= "$locale: primary_language='$lang'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: primary_language='uk'
+root: primary_language='root'
+uk@currency=EURO: primary_language='uk'
+Hindi: primary_language='hindi'
+de: primary_language='de'
+fr: primary_language='fr'
+ja: primary_language='ja'
+i-enochian: primary_language='i-enochian'
+zh-Hant: primary_language='zh'
+zh-Hans: primary_language='zh'
+sr-Cyrl: primary_language='sr'
+sr-Latn: primary_language='sr'
+zh-Hans-CN: primary_language='zh'
+sr-Latn-CS: primary_language='sr'
+sl-rozaj: primary_language='sl'
+sl-nedis: primary_language='sl'
+de-CH-1901: primary_language='de'
+sl-IT-nedis: primary_language='sl'
+sl-Latn-IT-nedis: primary_language='sl'
+de-DE: primary_language='de'
+en-US: primary_language='en'
+es-419: primary_language='es'
+de-CH-x-phonebk: primary_language='de'
+az-Arab-x-AZE-derbend: primary_language='az'
+zh-min: primary_language='zh-min'
+zh-min-nan-Hant-CN: primary_language='zh'
+qaa-Qaaa-QM-x-southern: primary_language='qaa'
+sr-Latn-QM: primary_language='sr'
+sr-Qaaa-CS: primary_language='sr'
+en-US-u-islamCal: primary_language='en'
+zh-CN-a-myExt-x-private: primary_language='zh'
+en-a-myExt-b-another: primary_language='en'
+de-419-DE: primary_language='de'
+a-DE: primary_language='a'
+ar-a-aaa-b-bbb-a-ccc: primary_language='ar'
diff --git a/ext/intl/tests/locale_get_region.phpt b/ext/intl/tests/locale_get_region.phpt
new file mode 100644
index 0000000..0baae49
--- /dev/null
+++ b/ext/intl/tests/locale_get_region.phpt
@@ -0,0 +1,123 @@
+--TEST--
+locale_get_region()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the region for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $scr = ut_loc_get_region( $locale);
+ $res_str .= "$locale: region='$scr'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECTF--
+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='%r(ROZAJ)?%r'
+sl-nedis: region='%r(NEDIS)?%r'
+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='%r(X)?%r'
+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='%r(A)?%r'
+de-419-DE: region='419'
+a-DE: region='DE'
+ar-a-aaa-b-bbb-a-ccc: region='%r(A)?%r'
diff --git a/ext/intl/tests/locale_get_script.phpt b/ext/intl/tests/locale_get_script.phpt
new file mode 100644
index 0000000..6a794fd
--- /dev/null
+++ b/ext/intl/tests/locale_get_script.phpt
@@ -0,0 +1,122 @@
+--TEST--
+locale_get_script()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try getting the prmary language for different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+/*Tags that use extensions (examples ONLY: extensions MUST be defined
+ by revision or update to this document or by RFC): */
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $scr = ut_loc_get_script( $locale);
+ $res_str .= "$locale: script='$scr'";
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: script=''
+root: script=''
+uk@currency=EURO: script=''
+Hindi: script=''
+de: script=''
+fr: script=''
+ja: script=''
+i-enochian: script=''
+zh-Hant: script='Hant'
+zh-Hans: script='Hans'
+sr-Cyrl: script='Cyrl'
+sr-Latn: script='Latn'
+zh-Hans-CN: script='Hans'
+sr-Latn-CS: script='Latn'
+sl-rozaj: script=''
+sl-nedis: script=''
+de-CH-1901: script=''
+sl-IT-nedis: script=''
+sl-Latn-IT-nedis: script='Latn'
+de-DE: script=''
+en-US: script=''
+es-419: script=''
+de-CH-x-phonebk: script=''
+az-Arab-x-AZE-derbend: script='Arab'
+zh-min: script=''
+zh-min-nan-Hant-CN: script=''
+x-whatever: script=''
+qaa-Qaaa-QM-x-southern: script='Qaaa'
+sr-Latn-QM: script='Latn'
+sr-Qaaa-CS: script='Qaaa'
+en-US-u-islamCal: script=''
+zh-CN-a-myExt-x-private: script=''
+en-a-myExt-b-another: script=''
+de-419-DE: script=''
+a-DE: script=''
+ar-a-aaa-b-bbb-a-ccc: script=''
+
diff --git a/ext/intl/tests/locale_lookup.phpt b/ext/intl/tests/locale_lookup.phpt
new file mode 100644
index 0000000..f0affaf
--- /dev/null
+++ b/ext/intl/tests/locale_lookup.phpt
@@ -0,0 +1,99 @@
+--TEST--
+locale_lookup.phpt()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try parsing different Locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $loc_ranges = array(
+ 'de-de',
+ 'sl_IT',
+ 'sl_IT_Nedis',
+ 'jbo',
+ 'art-lojban'
+ );
+
+ $lang_tags = array(
+ 'de-DEVA',
+ 'de-DE-1996',
+ 'de-DE',
+ 'zh_Hans',
+ 'de-CH-1996',
+ 'sl_IT',
+ 'sl_IT_nedis-a-kirti-x-xyz',
+ 'sl_IT_rozaj',
+ 'sl_IT_NEDIS_ROJAZ_1901',
+ 'i-enochian',
+ 'sgn-CH-de',
+ 'art-lojban',
+ 'i-lux',
+ 'art-lojban',
+ 'jbo',
+ 'en_sl_IT',
+ 'zh-Hant-CN-x-prv1-prv2'
+ );
+
+
+ $res_str = '';
+ $isCanonical = false;
+
+ foreach($loc_ranges as $loc_range){
+ $res_str .="--------------\n";
+ $result= ut_loc_locale_lookup( $lang_tags , $loc_range,$isCanonical,"en_US");
+ $comma_arr =implode(",",$lang_tags);
+ $res_str .= "loc_range:$loc_range \nlang_tags: $comma_arr\n";
+ $res_str .= "\nlookup result:$result\n";
+//canonicalized version
+ $result= ut_loc_locale_lookup( $lang_tags , $loc_range,!($isCanonical),"en_US");
+ $can_loc_range = ut_loc_canonicalize($loc_range);
+ $res_str .= "Canonical lookup result:$result\n";
+
+ }
+
+ $res_str .= "\n";
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+--------------
+loc_range:de-de
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:de-DE
+Canonical lookup result:de_de
+--------------
+loc_range:sl_IT
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:sl_IT
+Canonical lookup result:sl_it
+--------------
+loc_range:sl_IT_Nedis
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:sl_IT
+Canonical lookup result:sl_it
+--------------
+loc_range:jbo
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:jbo
+Canonical lookup result:jbo
+--------------
+loc_range:art-lojban
+lang_tags: de-DEVA,de-DE-1996,de-DE,zh_Hans,de-CH-1996,sl_IT,sl_IT_nedis-a-kirti-x-xyz,sl_IT_rozaj,sl_IT_NEDIS_ROJAZ_1901,i-enochian,sgn-CH-de,art-lojban,i-lux,art-lojban,jbo,en_sl_IT,zh-Hant-CN-x-prv1-prv2
+
+lookup result:art-lojban
+Canonical lookup result:jbo
diff --git a/ext/intl/tests/locale_parse_locale.phpt b/ext/intl/tests/locale_parse_locale.phpt
new file mode 100644
index 0000000..c8aa2f4
--- /dev/null
+++ b/ext/intl/tests/locale_parse_locale.phpt
@@ -0,0 +1,203 @@
+--TEST--
+locale_parse_locale() icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) 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' , ";
+ }
+ $res_str = rtrim($res_str);
+ }
+ 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();
+
+?>
+--EXPECTF--
+---------------------
+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' ,%r( region : 'ROZAJ' ,)?%r
+---------------------
+sl-nedis:
+language : 'sl' ,%r( region : 'NEDIS' ,)?%r
+---------------------
+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_parse_locale2.phpt b/ext/intl/tests/locale_parse_locale2.phpt
new file mode 100644
index 0000000..6012862
--- /dev/null
+++ b/ext/intl/tests/locale_parse_locale2.phpt
@@ -0,0 +1,203 @@
+--TEST--
+locale_parse_locale() icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) 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' , ";
+ }
+ $res_str = rtrim($res_str);
+ }
+ 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();
+
+?>
+--EXPECTF--
+---------------------
+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' , variant0 : 'ROZAJ' ,
+---------------------
+sl-nedis:
+language : 'sl' , variant0 : '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 100644
index 0000000..7debf4d
--- /dev/null
+++ b/ext/intl/tests/locale_set_default.phpt
@@ -0,0 +1,133 @@
+--TEST--
+locale_set_default($locale)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try setting the default Locale with different locales
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $locales = array(
+ 'uk-ua_CALIFORNIA@currency=;currency=GRN',
+ 'root',
+ 'uk@currency=EURO',
+ 'Hindi',
+//Simple language subtag
+ 'de',
+ 'fr',
+ 'ja',
+ 'i-enochian', //(example of a grandfathered tag)
+//Language subtag plus Script subtag:
+ 'zh-Hant',
+ 'zh-Hans',
+ 'sr-Cyrl',
+ 'sr-Latn',
+//Language-Script-Region
+ 'zh-Hans-CN',
+ 'sr-Latn-CS',
+//Language-Variant
+ 'sl-rozaj',
+ 'sl-nedis',
+//Language-Region-Variant
+ 'de-CH-1901',
+ 'sl-IT-nedis',
+//Language-Script-Region-Variant
+ 'sl-Latn-IT-nedis',
+//Language-Region:
+ 'de-DE',
+ 'en-US',
+ 'es-419',
+//Private use subtags:
+ 'de-CH-x-phonebk',
+ 'az-Arab-x-AZE-derbend',
+//Extended language subtags
+ 'zh-min',
+ 'zh-min-nan-Hant-CN',
+//Private use registry values
+ 'x-whatever',
+ 'qaa-Qaaa-QM-x-southern',
+ 'sr-Latn-QM',
+ 'sr-Qaaa-CS',
+//Tags that use extensions (examples ONLY: extensions MUST be defined
+// by revision or update to this document or by RFC):
+ 'en-US-u-islamCal',
+ 'zh-CN-a-myExt-x-private',
+ 'en-a-myExt-b-another',
+//Some Invalid Tags:
+ 'de-419-DE',
+ 'a-DE',
+ 'ar-a-aaa-b-bbb-a-ccc'
+ );
+
+/*
+ $locales = array(
+ 'es'
+ );
+*/
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $isSuccessful = ut_loc_set_default( $locale);
+ if ($isSuccessful ){
+ $lang = ut_loc_get_default( );
+ $res_str .= "$locale: set locale '$lang'";
+ }
+ else{
+ $res_str .= "$locale: Error in set locale";
+ }
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+uk-ua_CALIFORNIA@currency=;currency=GRN: set locale 'uk-ua_CALIFORNIA@currency=;currency=GRN'
+root: set locale 'root'
+uk@currency=EURO: set locale 'uk@currency=EURO'
+Hindi: set locale 'Hindi'
+de: set locale 'de'
+fr: set locale 'fr'
+ja: set locale 'ja'
+i-enochian: set locale 'i-enochian'
+zh-Hant: set locale 'zh-Hant'
+zh-Hans: set locale 'zh-Hans'
+sr-Cyrl: set locale 'sr-Cyrl'
+sr-Latn: set locale 'sr-Latn'
+zh-Hans-CN: set locale 'zh-Hans-CN'
+sr-Latn-CS: set locale 'sr-Latn-CS'
+sl-rozaj: set locale 'sl-rozaj'
+sl-nedis: set locale 'sl-nedis'
+de-CH-1901: set locale 'de-CH-1901'
+sl-IT-nedis: set locale 'sl-IT-nedis'
+sl-Latn-IT-nedis: set locale 'sl-Latn-IT-nedis'
+de-DE: set locale 'de-DE'
+en-US: set locale 'en-US'
+es-419: set locale 'es-419'
+de-CH-x-phonebk: set locale 'de-CH-x-phonebk'
+az-Arab-x-AZE-derbend: set locale 'az-Arab-x-AZE-derbend'
+zh-min: set locale 'zh-min'
+zh-min-nan-Hant-CN: set locale 'zh-min-nan-Hant-CN'
+x-whatever: set locale 'x-whatever'
+qaa-Qaaa-QM-x-southern: set locale 'qaa-Qaaa-QM-x-southern'
+sr-Latn-QM: set locale 'sr-Latn-QM'
+sr-Qaaa-CS: set locale 'sr-Qaaa-CS'
+en-US-u-islamCal: set locale 'en-US-u-islamCal'
+zh-CN-a-myExt-x-private: set locale 'zh-CN-a-myExt-x-private'
+en-a-myExt-b-another: set locale 'en-a-myExt-b-another'
+de-419-DE: set locale 'de-419-DE'
+a-DE: set locale 'a-DE'
+ar-a-aaa-b-bbb-a-ccc: set locale 'ar-a-aaa-b-bbb-a-ccc'
diff --git a/ext/intl/tests/msgfmt_clone.phpt b/ext/intl/tests/msgfmt_clone.phpt
new file mode 100644
index 0000000..9d8ea64
--- /dev/null
+++ b/ext/intl/tests/msgfmt_clone.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Cloning msgfmt
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+include_once( 'ut_common.inc' );
+$GLOBALS['oo-mode'] = true;
+$res_str = '';
+/*
+ * Clone
+ */
+$fmt = ut_msgfmt_create( "en_US", "{0,number} monkeys on {1,number} trees" );
+
+// Get default patten.
+$res_str .= "Formatting result: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+$fmt_clone = clone $fmt;
+// Set a new pattern.
+$pattern = "{0,number} trees hosting {1,number} monkeys";
+$res = ut_msgfmt_set_pattern( $fmt, $pattern );
+$res_str .= "Formatting result: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+$res_str .= "Formatting clone result: " . ut_msgfmt_format( $fmt_clone, array(123, 456) ) . "\n";
+
+echo $res_str;
+
+?>
+--EXPECTF--
+Formatting result: 123 monkeys on 456 trees
+Formatting result: 123 trees hosting 456 monkeys
+Formatting clone result: 123 monkeys on 456 trees
diff --git a/ext/intl/tests/msgfmt_clone_bad_obj.phpt b/ext/intl/tests/msgfmt_clone_bad_obj.phpt
new file mode 100644
index 0000000..4832109
--- /dev/null
+++ b/ext/intl/tests/msgfmt_clone_bad_obj.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Cloning unconstructed MessageFormatter
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+class A extends MessageFormatter {
+ function __construct() {}
+}
+
+$a = new A;
+try {
+ $b = clone $a;
+} catch (Exception $e) {
+ var_dump($e->getMessage());
+}
+
+--EXPECTF--
+string(%d) "Cannot clone unconstructed MessageFormatter"
diff --git a/ext/intl/tests/msgfmt_fail.phpt b/ext/intl/tests/msgfmt_fail.phpt
new file mode 100644
index 0000000..bffb71c
--- /dev/null
+++ b/ext/intl/tests/msgfmt_fail.phpt
@@ -0,0 +1,102 @@
+--TEST--
+msgfmt creation failures icu <= 4.2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.3', '<') != 1) print 'skip'; ?>
+--FILE--
+<?php
+
+function err($fmt) {
+ if(!$fmt) {
+ echo var_export(intl_get_error_message(), true)."\n";
+ }
+}
+
+function crt($t, $l, $s) {
+ switch(true) {
+ case $t == "O":
+ return new MessageFormatter($l, $s);
+ break;
+ case $t == "C":
+ return MessageFormatter::create($l, $s);
+ break;
+ case $t == "P":
+ return msgfmt_create($l, $s);
+ break;
+ }
+}
+
+$args = array(
+ array(null, null),
+ array("whatever", "{0,whatever}"),
+ array(array(), array()),
+ array("en", "{0,choice}"),
+ array("fr", "{0,"),
+ array("en_US", "\xD0"),
+);
+
+$fmt = new MessageFormatter();
+err($fmt);
+$fmt = msgfmt_create();
+err($fmt);
+$fmt = MessageFormatter::create();
+err($fmt);
+$fmt = new MessageFormatter('en');
+err($fmt);
+$fmt = msgfmt_create('en');
+err($fmt);
+$fmt = MessageFormatter::create('en');
+err($fmt);
+
+foreach($args as $arg) {
+ $fmt = crt("O", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("C", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("P", $arg[0], $arg[1]);
+ err($fmt);
+}
+
+?>
+--EXPECTF--
+Warning: MessageFormatter::__construct() expects exactly 2 parameters, 0 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: 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
+'msgfmt_create: unable to parse input parameters: 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'
+'msgfmt_create: 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'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::__construct() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: msgfmt_create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: 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'
+'msgfmt_create: 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'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
diff --git a/ext/intl/tests/msgfmt_fail2.phpt b/ext/intl/tests/msgfmt_fail2.phpt
new file mode 100644
index 0000000..eee2424
--- /dev/null
+++ b/ext/intl/tests/msgfmt_fail2.phpt
@@ -0,0 +1,102 @@
+--TEST--
+msgfmt creation failures icu >= 4.8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?>
+--FILE--
+<?php
+
+function err($fmt) {
+ if(!$fmt) {
+ echo var_export(intl_get_error_message(), true)."\n";
+ }
+}
+
+function crt($t, $l, $s) {
+ switch(true) {
+ case $t == "O":
+ return new MessageFormatter($l, $s);
+ break;
+ case $t == "C":
+ return MessageFormatter::create($l, $s);
+ break;
+ case $t == "P":
+ return msgfmt_create($l, $s);
+ break;
+ }
+}
+
+$args = array(
+ array(null, null),
+ array("whatever", "{0,whatever}"),
+ array(array(), array()),
+ array("en", "{0,choice}"),
+ array("fr", "{0,"),
+ array("en_US", "\xD0"),
+);
+
+$fmt = new MessageFormatter();
+err($fmt);
+$fmt = msgfmt_create();
+err($fmt);
+$fmt = MessageFormatter::create();
+err($fmt);
+$fmt = new MessageFormatter('en');
+err($fmt);
+$fmt = msgfmt_create('en');
+err($fmt);
+$fmt = MessageFormatter::create('en');
+err($fmt);
+
+foreach($args as $arg) {
+ $fmt = crt("O", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("C", $arg[0], $arg[1]);
+ err($fmt);
+ $fmt = crt("P", $arg[0], $arg[1]);
+ err($fmt);
+}
+
+?>
+--EXPECTF--
+Warning: MessageFormatter::__construct() expects exactly 2 parameters, 0 given in %s on line %d
+'msgfmt_create: unable to parse input parameters: 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
+'msgfmt_create: unable to parse input parameters: 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'
+'msgfmt_create: 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'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::__construct() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: MessageFormatter::create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+
+Warning: msgfmt_create() expects parameter 1 to be string, array given in %s on line %d
+'msgfmt_create: unable to parse input parameters: U_ILLEGAL_ARGUMENT_ERROR'
+'msgfmt_create: message formatter creation failed: U_PATTERN_SYNTAX_ERROR'
+'msgfmt_create: message formatter creation failed: U_PATTERN_SYNTAX_ERROR'
+'msgfmt_create: message formatter creation failed: U_PATTERN_SYNTAX_ERROR'
+'msgfmt_create: 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'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
+'msgfmt_create: error converting pattern to UTF-16: U_INVALID_CHAR_FOUND'
diff --git a/ext/intl/tests/msgfmt_format.phpt b/ext/intl/tests/msgfmt_format.phpt
new file mode 100644
index 0000000..c1bf5e9
--- /dev/null
+++ b/ext/intl/tests/msgfmt_format.phpt
@@ -0,0 +1,70 @@
+--TEST--
+msgfmt_format()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_US' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'ru_UA' => "{0,number,integer} мавп на {1,number,integer} деревах це {2,number} мавпи на кожному деревi",
+ 'de' => "{0,number,integer} Affen über {1,number,integer} Bäume um {2,number} Affen pro Baum",
+ 'en_UK' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'root' => '{0,whatever} would not work!',
+ 'fr' => "C'est la vie!",
+ );
+
+ $str_res = '';
+ $m = 4560;
+ $t = 123;
+
+ foreach( $locales as $locale => $pattern )
+ {
+ $str_res .= "\nLocale is: $locale\n";
+ $fmt = ut_msgfmt_create( $locale, $pattern );
+ if(!$fmt) {
+ $str_res .= dump(intl_get_error_message())."\n";
+ continue;
+ }
+ $str_res .= dump( ut_msgfmt_format( $fmt, array($m, $t, $m/$t) ) ) . "\n";
+ $str_res .= dump( ut_msgfmt_format_message($locale, $pattern, array($m, $t, $m/$t))) . "\n";
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Locale is: en_US
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+
+Locale is: ru_UA
+'4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi'
+'4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi'
+
+Locale is: de
+'4.560 Affen über 123 Bäume um 37,073 Affen pro Baum'
+'4.560 Affen über 123 Bäume um 37,073 Affen pro Baum'
+
+Locale is: en_UK
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+'4,560 monkeys on 123 trees make 37.073 monkeys per tree'
+
+Locale is: root
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Locale is: fr
+'C\'est la vie!'
+'C\'est la vie!'
diff --git a/ext/intl/tests/msgfmt_get_error.phpt b/ext/intl/tests/msgfmt_get_error.phpt
new file mode 100644
index 0000000..015c50d
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_error.phpt
@@ -0,0 +1,29 @@
+--TEST--
+msgmfmt_get_error_message/code()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Error handling.
+ */
+
+
+function ut_main()
+{
+ $fmt = ut_msgfmt_create( "en_US", "{0, number} monkeys on {1, number} trees" );
+ $num = ut_msgfmt_format( $fmt, array());
+ if( $num === false )
+ return $fmt->getErrorMessage() . " (" . $fmt->getErrorCode() . ")\n";
+ else
+ return "Ooops, an error should have occured.";
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+msgfmt_format: not enough parameters: U_ILLEGAL_ARGUMENT_ERROR (1)
diff --git a/ext/intl/tests/msgfmt_get_locale.phpt b/ext/intl/tests/msgfmt_get_locale.phpt
new file mode 100644
index 0000000..4c2651f
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_locale.phpt
@@ -0,0 +1,40 @@
+--TEST--
+msgfmt_get_locale()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get locale.
+ */
+
+function ut_main()
+{
+ $locales = array(
+ 'en_UK',
+ 'en_US@California',
+ 'uk',
+ );
+
+ $res_str = '';
+
+ foreach( $locales as $locale )
+ {
+ $fmt = ut_msgfmt_create( $locale, "Test" );
+ $res_str .= "$locale: " . dump( ut_msgfmt_get_locale( $fmt ) );
+ $res_str .= "\n";
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+?>
+--EXPECT--
+en_UK: 'en_UK'
+en_US@California: 'en_US@California'
+uk: 'uk'
diff --git a/ext/intl/tests/msgfmt_get_set_pattern.phpt b/ext/intl/tests/msgfmt_get_set_pattern.phpt
new file mode 100644
index 0000000..67b1aca
--- /dev/null
+++ b/ext/intl/tests/msgfmt_get_set_pattern.phpt
@@ -0,0 +1,53 @@
+--TEST--
+msgfmt_get/set_pattern()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Get/set pattern.
+ */
+
+
+function ut_main()
+{
+ $res_str = '';
+ $fmt = ut_msgfmt_create( "en_US", "{0,number} monkeys on {1,number} trees" );
+
+ // Get default patten.
+ $res_str .= "Default pattern: '" . ut_msgfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatting result: " . ut_msgfmt_format( $fmt, array(123, 456) ) . "\n";
+
+ // Set a new pattern.
+ $pattern = "{0,number} trees hosting {1,number} monkeys";
+ $res = ut_msgfmt_set_pattern( $fmt, $pattern );
+ if( $res === false )
+ $res_str .= ut_msgfmt_get_error_message( $fmt ) . " (" . ut_msgfmt_get_error_code( $fmt ) . ")\n";
+
+ // Check if the pattern has been changed.
+ $res = ut_msgfmt_get_pattern( $fmt );
+ if( $res === false )
+ $res_str .= ut_msgfmt_get_error_message( $fmt ) . " (" . ut_msgfmt_get_error_code( $fmt ) . ")\n";
+ $res_str .= "New pattern: '" . ut_msgfmt_get_pattern( $fmt ) . "'\n";
+ $res_str .= "Formatted message: " . 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 message: " . 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 message: 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 message: 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 100644
index 0000000..b9ec363
--- /dev/null
+++ b/ext/intl/tests/msgfmt_parse.phpt
@@ -0,0 +1,114 @@
+--TEST--
+msgfmt_parse() tests
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Format a number using misc locales/patterns.
+ */
+
+
+function ut_main()
+{
+ $locales = array(
+ 'en_US' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'ru_UA' => "{0,number,integer} мавп на {1,number,integer} деревах це {2,number} мавпи на кожному деревi",
+ 'de' => "{0,number,integer} Affen über {1,number,integer} Bäume um {2,number} Affen pro Baum",
+ 'en_UK' => "{0,number,integer} monkeys on {1,number,integer} trees make {2,number} monkeys per tree",
+ 'root' => '{0,whatever} would not work!',
+ 'fr' => 'C\'est {0,number,integer}',
+ );
+
+ $results = array(
+ 'en_US' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'ru_UA' => "4 560 мавп на 123 деревах це 37,073 мавпи на кожному деревi",
+ 'de' => "4.560 Affen über 123 Bäume um 37,073 Affen pro Baum",
+ 'en_UK' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'root' => "4,560 monkeys on 123 trees make 37.073 monkeys per tree",
+ 'fr' => "C'est 42",
+
+ );
+
+ $str_res = '';
+
+ foreach( $locales as $locale => $pattern )
+ {
+ $str_res .= "\nLocale is: $locale\n";
+ $fmt = ut_msgfmt_create( $locale, $pattern );
+ if(!$fmt) {
+ $str_res .= dump(intl_get_error_message())."\n";
+ continue;
+ }
+ $str_res .= dump( ut_msgfmt_parse( $fmt, $results[$locale] ) ) . "\n";
+ $str_res .= dump( ut_msgfmt_parse_message($locale, $pattern, $results[$locale])) . "\n";
+ }
+ return $str_res;
+}
+
+include_once( 'ut_common.inc' );
+
+// Run the test
+ut_run();
+
+?>
+--EXPECT--
+Locale is: en_US
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: ru_UA
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: de
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: en_UK
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+array (
+ 0 => 4560,
+ 1 => 123,
+ 2 => 37.073,
+)
+
+Locale is: root
+'msgfmt_create: message formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR'
+
+Locale is: fr
+array (
+ 0 => 42,
+)
+array (
+ 0 => 42,
+)
diff --git a/ext/intl/tests/normalizer_normalize.phpt b/ext/intl/tests/normalizer_normalize.phpt
new file mode 100644
index 0000000..238b742
--- /dev/null
+++ b/ext/intl/tests/normalizer_normalize.phpt
@@ -0,0 +1,160 @@
+--TEST--
+normalize()
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+/*
+ * Try normalization and test normalization
+ * with Procedural and Object methods.
+ */
+
+function ut_main()
+{
+ $res_str = '';
+
+ $forms = array(
+ Normalizer::FORM_C,
+ Normalizer::FORM_D,
+ Normalizer::FORM_KC,
+ Normalizer::FORM_KD,
+ Normalizer::NONE,
+ );
+
+ $forms_str = array (
+ Normalizer::FORM_C => 'UNORM_FORM_C',
+ Normalizer::FORM_D => 'UNORM_FORM_D',
+ Normalizer::FORM_KC => 'UNORM_FORM_KC',
+ Normalizer::FORM_KD => 'UNORM_FORM_KD',
+ Normalizer::NONE => 'UNORM_NONE',
+ );
+
+ /* just make sure all the form constants are defined as in the api spec */
+ if ( Normalizer::FORM_C != Normalizer::NFC ||
+ Normalizer::FORM_D != Normalizer::NFD ||
+ Normalizer::FORM_KC != Normalizer::NFKC ||
+ Normalizer::FORM_KD != Normalizer::NFKD ||
+ Normalizer::NONE == Normalizer::FORM_C ) {
+
+ $res_str .= "Invalid normalization form declarations!\n";
+ }
+
+ $char_a_diaeresis = "\xC3\xA4"; // 'LATIN SMALL LETTER A WITH DIAERESIS' (U+00E4)
+ $char_a_ring = "\xC3\xA5"; // 'LATIN SMALL LETTER A WITH RING ABOVE' (U+00E5)
+ $char_o_diaeresis = "\xC3\xB6"; // 'LATIN SMALL LETTER O WITH DIAERESIS' (U+00F6)
+
+ $char_angstrom_sign = "\xE2\x84\xAB"; // 'ANGSTROM SIGN' (U+212B)
+ $char_A_ring = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
+
+ $char_ohm_sign = "\xE2\x84\xA6"; // 'OHM SIGN' (U+2126)
+ $char_omega = "\xCE\xA9"; // 'GREEK CAPITAL LETTER OMEGA' (U+03A9)
+
+ $char_combining_ring_above = "\xCC\x8A"; // 'COMBINING RING ABOVE' (U+030A)
+
+ $char_fi_ligature = "\xEF\xAC\x81"; // 'LATIN SMALL LIGATURE FI' (U+FB01)
+
+ $char_long_s_dot = "\xE1\xBA\x9B"; // 'LATIN SMALL LETTER LONG S WITH DOT ABOVE' (U+1E9B)
+
+ $strs = array(
+ 'ABC',
+ $char_a_diaeresis . '||' . $char_a_ring . '||' . $char_o_diaeresis,
+ $char_angstrom_sign . '||' . $char_A_ring . '||' . 'A' . $char_combining_ring_above,
+ $char_ohm_sign . '||' . $char_omega,
+ $char_fi_ligature,
+ $char_long_s_dot,
+ );
+
+ foreach( $forms as $form )
+ {
+ foreach( $strs as $str )
+ {
+ $str_norm = ut_norm_normalize( $str, $form );
+ $error_code = intl_get_error_code();
+ $error_message = intl_get_error_message();
+
+ $str_hex = urlencode($str);
+ $str_norm_hex = urlencode($str_norm);
+ $res_str .= "'$str_hex' normalized to form '{$forms_str[$form]}' is '$str_norm_hex'"
+ . "\terror info: '$error_message' ($error_code)\n"
+ . "";
+
+ $is_norm = ut_norm_is_normalized( $str, $form );
+ $error_code = intl_get_error_code();
+ $error_message = intl_get_error_message();
+
+ $res_str .= " is in form '{$forms_str[$form]}'? = " . ($is_norm ? "yes" : "no")
+ . "\terror info: '$error_message' ($error_code)\n"
+ . "";
+ }
+ }
+
+ return $res_str;
+}
+
+include_once( 'ut_common.inc' );
+ut_run();
+
+?>
+--EXPECT--
+'ABC' normalized to form 'UNORM_FORM_C' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_C' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_C' is '%C3%85%7C%7C%C3%85%7C%7C%C3%85' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_C' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_C' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_C' is '%E1%BA%9B' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_C'? = yes error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_D' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_D' is 'a%CC%88%7C%7Ca%CC%8A%7C%7Co%CC%88' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_D' is 'A%CC%8A%7C%7CA%CC%8A%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_D' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_D' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_D' is '%C5%BF%CC%87' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_D'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_KC' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_KC' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = yes error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_KC' is '%C3%85%7C%7C%C3%85%7C%7C%C3%85' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_KC' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_KC' is 'fi' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_KC' is '%E1%B9%A1' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KC'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_FORM_KD' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = yes error info: 'U_ZERO_ERROR' (0)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_FORM_KD' is 'a%CC%88%7C%7Ca%CC%8A%7C%7Co%CC%88' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_FORM_KD' is 'A%CC%8A%7C%7CA%CC%8A%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_FORM_KD' is '%CE%A9%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%EF%AC%81' normalized to form 'UNORM_FORM_KD' is 'fi' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'%E1%BA%9B' normalized to form 'UNORM_FORM_KD' is 's%CC%87' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_FORM_KD'? = no error info: 'U_ZERO_ERROR' (0)
+'ABC' normalized to form 'UNORM_NONE' is 'ABC' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' normalized to form 'UNORM_NONE' is '%C3%A4%7C%7C%C3%A5%7C%7C%C3%B6' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' normalized to form 'UNORM_NONE' is '%E2%84%AB%7C%7C%C3%85%7C%7CA%CC%8A' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E2%84%A6%7C%7C%CE%A9' normalized to form 'UNORM_NONE' is '%E2%84%A6%7C%7C%CE%A9' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%EF%AC%81' normalized to form 'UNORM_NONE' is '%EF%AC%81' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+'%E1%BA%9B' normalized to form 'UNORM_NONE' is '%E1%BA%9B' error info: 'U_ZERO_ERROR' (0)
+ is in form 'UNORM_NONE'? = no error info: 'normalizer_normalize: illegal normalization form: U_ILLEGAL_ARGUMENT_ERROR' (1)
+
diff --git a/ext/intl/tests/regression_sort_and_cow.phpt b/ext/intl/tests/regression_sort_and_cow.phpt
new file mode 100644
index 0000000..d7ff797
--- /dev/null
+++ b/ext/intl/tests/regression_sort_and_cow.phpt
@@ -0,0 +1,85 @@
+--TEST--
+Regression: sort() and copy-on-write.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check if collator_sort() properly supports copy-on-write.
+ */
+
+
+/* Create two copies of the given array.
+ * Sort the array and the first copy.
+ * Check if the second copy remains unsorted.
+ */
+function test_COW( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ // Create two copies of the given array.
+ $copy1 = $test_array;
+ $copy2 = $test_array;
+
+ // Sort given array and the first copy of it.
+ ut_coll_sort( $coll, $test_array );
+ ut_coll_sort( $coll, $copy1 );
+
+ // Return contents of all the arrays.
+ // The second copy should remain unsorted.
+ $res_str .= dump( $test_array ) . "\n";
+ $res_str .= dump( $copy1 ) . "\n";
+ $res_str .= dump( $copy2 ) . "\n";
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ $a1 = array( 'b', 'a', 'c' );
+ $a2 = array( 'в', 'а', 'б' );
+
+ $res_str .= test_COW( 'en_US', $a1 );
+ $res_str .= test_COW( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'b',
+ 1 => 'a',
+ 2 => 'c',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'в',
+ 1 => 'а',
+ 2 => 'б',
+)
diff --git a/ext/intl/tests/regression_sort_eq.phpt b/ext/intl/tests/regression_sort_eq.phpt
new file mode 100644
index 0000000..8676c5c
--- /dev/null
+++ b/ext/intl/tests/regression_sort_eq.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Regression: sort() eq but different len.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Test sorting strings that have different length but otherwise equal.
+ */
+
+function sort_using_locale( $locale, $test_array )
+{
+ $coll = ut_coll_create( $locale );
+
+ // Sort array.
+ ut_coll_sort( $coll, $test_array );
+
+ // And return the sorted array.
+ return dump( $test_array ) . "\n";
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Define a couple of arrays.
+ // Each array contains equal strings that differ only in their length.
+ $a1 = array( 'aa', 'aaa', 'a' );
+ $a2 = array( 'пп', 'ппп', 'п' );
+
+ // Sort them.
+ $res_str .= sort_using_locale( 'en_US', $a1 );
+ $res_str .= sort_using_locale( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+array (
+ 0 => 'п',
+ 1 => 'пп',
+ 2 => 'ппп',
+)
diff --git a/ext/intl/tests/regression_sortwsk_and_cow.phpt b/ext/intl/tests/regression_sortwsk_and_cow.phpt
new file mode 100644
index 0000000..a495323
--- /dev/null
+++ b/ext/intl/tests/regression_sortwsk_and_cow.phpt
@@ -0,0 +1,86 @@
+--TEST--
+Regression: sort_wsk() and copy-on-write.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Check if collator_sort_with_sort_keys()
+ * properly supports copy-on-write.
+ */
+
+
+/* Create two copies of the given array.
+ * Sort the array and the first copy.
+ * Check if the second copy remains unsorted.
+ */
+function test_COW( $locale, $test_array )
+{
+ $res_str = '';
+
+ $coll = ut_coll_create( $locale );
+
+ // Create two copies of the given array.
+ $copy1 = $test_array;
+ $copy2 = $test_array;
+
+ // Sort given array and the first copy of it.
+ ut_coll_sort_with_sort_keys( $coll, $test_array );
+ ut_coll_sort_with_sort_keys( $coll, $copy1 );
+
+ // Return contents of all the arrays.
+ // The second copy should remain unsorted.
+ $res_str .= dump( $test_array ) . "\n";
+ $res_str .= dump( $copy1 ) . "\n";
+ $res_str .= dump( $copy2 ) . "\n";
+
+ return $res_str;
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ $a1 = array( 'b', 'a', 'c' );
+ $a2 = array( 'в', 'а', 'б' );
+
+ $res_str .= test_COW( 'en_US', $a1 );
+ $res_str .= test_COW( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'a',
+ 1 => 'b',
+ 2 => 'c',
+)
+array (
+ 0 => 'b',
+ 1 => 'a',
+ 2 => 'c',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'а',
+ 1 => 'б',
+ 2 => 'в',
+)
+array (
+ 0 => 'в',
+ 1 => 'а',
+ 2 => 'б',
+)
diff --git a/ext/intl/tests/regression_sortwsk_eq.phpt b/ext/intl/tests/regression_sortwsk_eq.phpt
new file mode 100644
index 0000000..945bac3
--- /dev/null
+++ b/ext/intl/tests/regression_sortwsk_eq.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Regression: sort_wsk() eq but different len.
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+/*
+ * Test sorting strings that have different length but otherwise equal.
+ */
+
+function sort_using_locale( $locale, $test_array )
+{
+ $coll = ut_coll_create( $locale );
+
+ // Sort array.
+ ut_coll_sort_with_sort_keys( $coll, $test_array );
+
+ // And return the sorted array.
+ return dump( $test_array ) . "\n";
+}
+
+function ut_main()
+{
+ $res_str = '';
+
+ // Define a couple of arrays.
+ // Each array contains equal strings that differ only in their length.
+ $a1 = array( 'aa', 'aaa', 'a' );
+ $a2 = array( 'пп', 'ппп', 'п' );
+
+ // Sort them.
+ $res_str .= sort_using_locale( 'en_US', $a1 );
+ $res_str .= sort_using_locale( 'ru_RU', $a2 );
+
+ return $res_str;
+}
+
+require_once( 'ut_common.inc' );
+ut_run();
+?>
+--EXPECT--
+array (
+ 0 => 'a',
+ 1 => 'aa',
+ 2 => 'aaa',
+)
+array (
+ 0 => 'п',
+ 1 => 'пп',
+ 2 => 'ппп',
+)
diff --git a/ext/intl/tests/resourcebundle.build b/ext/intl/tests/resourcebundle.build
new file mode 100644
index 0000000..6a7eeae
--- /dev/null
+++ b/ext/intl/tests/resourcebundle.build
@@ -0,0 +1,33 @@
+<?php
+// THIS SCRIPT WILL REBUILD ResourceBundle bundles from source files
+
+// DEFINE YOUR ICU TOOLS PATH HERE
+define("ICU_DIR", "C:/PROJECTS/ICU40/BIN/");
+
+$here = dirname(__FILE__);
+
+$dir = new GlobIterator("$here/_files/*.txt", FilesystemIterator::KEY_AS_FILENAME);
+
+foreach($dir as $file) {
+ passthru( ICU_DIR."genrb -s $here/_files/ -d $here/_files/resourcebundle ".$file->getFileName());
+}
+
+$dir = new GlobIterator("$here/_files/resourcebundle/*.res", FilesystemIterator::KEY_AS_FILENAME);
+foreach($dir as $file) {
+ if($file->getFileName() == "res_index.res") continue;
+ $list[] = str_replace(".res", "", $file->getFileName());
+}
+
+$filelist = join(" {\"\"}\n", $list);
+$res_index = <<<END
+res_index:table(nofallback) {
+ InstalledLocales {
+$filelist {""}
+ }
+}
+END;
+file_put_contents("$here/_files/res_index.txt", $res_index);
+
+passthru( ICU_DIR."genrb -s $here/_files/ -d $here/_files/resourcebundle res_index.txt");
+
+// passthru(ICU_DIR."icupkg -tl -a $here/rb.txt -s $here/_files -d $here/_files new $here/_files/resourcebundle.dat"); \ No newline at end of file
diff --git a/ext/intl/tests/resourcebundle.inc b/ext/intl/tests/resourcebundle.inc
new file mode 100644
index 0000000..2ec138b
--- /dev/null
+++ b/ext/intl/tests/resourcebundle.inc
@@ -0,0 +1,13 @@
+<?php
+define('BUNDLE', dirname(__FILE__)."/_files/resourcebundle");
+
+function debug( $res ) {
+ if (is_null( $res )) {
+ $ret = "NULL\n";
+ }
+ else {
+ $ret = print_r( $res, true ). "\n";
+ }
+ return $ret . sprintf( "%5d: %s\n", intl_get_error_code(), intl_get_error_message() );
+}
+
diff --git a/ext/intl/tests/resourcebundle_arrayaccess.phpt b/ext/intl/tests/resourcebundle_arrayaccess.phpt
new file mode 100644
index 0000000..d035a8d
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_arrayaccess.phpt
@@ -0,0 +1,50 @@
+--TEST--
+Test ResourceBundle array access and count - existing/missing keys
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ include "resourcebundle.inc";
+
+ // fall back
+ $r = new ResourceBundle( 'en_US', BUNDLE );
+
+ printf( "length: %d\n", count($r) );
+ printf( "teststring: %s\n", $r['teststring'] );
+ printf( "testint: %d\n", $r['testint'] );
+
+ print_r( $r['testvector'] );
+
+ printf( "testbin: %s\n", bin2hex($r['testbin']) );
+
+ $r2 = $r['testtable'];
+ printf( "testtable: %d\n", $r2['major'] );
+
+ $r2 = $r['testarray'];
+ printf( "testarray: %s\n", $r2[2] );
+
+ $t = $r['nonexisting'];
+ echo debug( $t );
+?>
+--EXPECT--
+length: 6
+teststring: Hello World!
+testint: 2
+Array
+(
+ [0] => 1
+ [1] => 2
+ [2] => 3
+ [3] => 4
+ [4] => 5
+ [5] => 6
+ [6] => 7
+ [7] => 8
+ [8] => 9
+ [9] => 0
+)
+testbin: a1b2c3d4e5f67890
+testtable: 3
+testarray: string 3
+NULL
+ 2: Cannot load resource element 'nonexisting': U_MISSING_RESOURCE_ERROR
diff --git a/ext/intl/tests/resourcebundle_create.phpt b/ext/intl/tests/resourcebundle_create.phpt
new file mode 100644
index 0000000..2bf4f55
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_create.phpt
@@ -0,0 +1,62 @@
+--TEST--
+Test ResourceBundle::__construct() - existing/missing bundles/locales
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+include "resourcebundle.inc";
+
+function ut_main() {
+ $str_res = '';
+ // all fine
+ $r1 = ut_resourcebundle_create( 'root', BUNDLE );
+ $str_res .= debug( $r1 );
+ $str_res .= print_r( $r1['teststring'], true)."\n";
+
+ // non-root one
+ $r1 = ut_resourcebundle_create( 'es', BUNDLE );
+ $str_res .= debug( $r1 );
+ $str_res .= print_r( $r1['teststring'], true)."\n";
+
+ // fall back
+ $r1 = ut_resourcebundle_create( 'en_US', BUNDLE );
+ $str_res .= debug( $r1 );
+ $str_res .= print_r( $r1['testsring'], true);
+
+ // fall out
+ $r2 = ut_resourcebundle_create( 'en_US', BUNDLE, false );
+ $str_res .= debug( $r2 );
+
+ // missing
+ $r3 = ut_resourcebundle_create( 'en_US', 'nonexisting' );
+ $str_res .= debug( $r3 );
+
+ return $str_res;
+}
+
+ include_once( 'ut_common.inc' );
+ ut_run();
+?>
+--EXPECTF--
+ResourceBundle Object
+(
+)
+
+ 0: U_ZERO_ERROR
+Hello World!
+ResourceBundle Object
+(
+)
+
+ 0: U_ZERO_ERROR
+Hola Mundo!
+ResourceBundle Object
+(
+)
+
+ -127: U_USING_DEFAULT_WARNING
+NULL
+ 2: resourcebundle_ctor: Cannot load libICU resource bundle: U_MISSING_RESOURCE_ERROR
+NULL
+ 2: resourcebundle_ctor: Cannot load libICU resource bundle: U_MISSING_RESOURCE_ERROR
diff --git a/ext/intl/tests/resourcebundle_individual.phpt b/ext/intl/tests/resourcebundle_individual.phpt
new file mode 100644
index 0000000..dcbf1f5
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_individual.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Test ResourceBundle::get() and length() - existing/missing keys
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ include "resourcebundle.inc";
+
+function ut_main() {
+ $str_res = '';
+ // fall back
+ $r = ut_resourcebundle_create( 'en_US', BUNDLE );
+
+ $str_res .= sprintf( "length: %d\n", ut_resourcebundle_count($r) );
+ $str_res .= sprintf( "teststring: %s\n", ut_resourcebundle_get($r, 'teststring' ) );
+ $str_res .= sprintf( "testint: %d\n", ut_resourcebundle_get($r, 'testint' ) );
+
+ $str_res .= print_r( ut_resourcebundle_get($r, 'testvector' ), true );
+
+ $str_res .= sprintf( "testbin: %s\n", bin2hex(ut_resourcebundle_get( $r,'testbin' )) );
+
+ $r2 = ut_resourcebundle_get($r, 'testtable' );
+ $str_res .= sprintf( "testtable: %d\n", ut_resourcebundle_get($r2, 'major' ) );
+
+ $r2 = ut_resourcebundle_get($r,'testarray' );
+ $str_res .= sprintf( "testarray: %s\n", ut_resourcebundle_get($r2, 2 ) );
+
+ $t = ut_resourcebundle_get( $r, 'nonexisting' );
+ $str_res .= debug( $t );
+
+ return $str_res;
+}
+ include_once( 'ut_common.inc' );
+ ut_run();
+?>
+--EXPECT--
+length: 6
+teststring: Hello World!
+testint: 2
+Array
+(
+ [0] => 1
+ [1] => 2
+ [2] => 3
+ [3] => 4
+ [4] => 5
+ [5] => 6
+ [6] => 7
+ [7] => 8
+ [8] => 9
+ [9] => 0
+)
+testbin: a1b2c3d4e5f67890
+testtable: 3
+testarray: string 3
+NULL
+ 2: Cannot load resource element 'nonexisting': U_MISSING_RESOURCE_ERROR
diff --git a/ext/intl/tests/resourcebundle_internal.phpt b/ext/intl/tests/resourcebundle_internal.phpt
new file mode 100644
index 0000000..fe90081
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_internal.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Test ResourceBundle::__construct() with internal ICU bundles
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) || !defined('INTL_ICU_DATA_VERSION') || version_compare(INTL_ICU_DATA_VERSION, '4.4', '<') ) print 'skip'; ?>
+--FILE--
+<?php
+$b = new ResourceBundle('de_DE', 'ICUDATA-region');
+var_dump($b->get('Countries')->get('DE'));
+
+$b = new ResourceBundle('icuver', 'ICUDATA');
+var_dump($b->get('ICUVersion') !== NULL);
+
+$b = new ResourceBundle('supplementalData', 'ICUDATA', false);
+var_dump($b->get('cldrVersion') !== NULL);
+?>
+--EXPECTF--
+string(11) "Deutschland"
+bool(true)
+bool(true)
diff --git a/ext/intl/tests/resourcebundle_iterator.phpt b/ext/intl/tests/resourcebundle_iterator.phpt
new file mode 100644
index 0000000..a10b9de
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_iterator.phpt
@@ -0,0 +1,73 @@
+--TEST--
+Test ResourceBundle iterator
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ include "resourcebundle.inc";
+
+ // fall back
+ $r = new ResourceBundle( 'en_US', BUNDLE );
+
+ foreach ($r as $onekey => $oneval) {
+ echo "Here comes $onekey:\n";
+ switch (gettype($oneval)) {
+ case 'string':
+ echo bin2hex( $oneval ) . "\n";
+ break;
+
+ case 'integer':
+ echo "$oneval\n";
+ break;
+
+ default:
+ print_r( $oneval );
+ }
+ echo "\n";
+ }
+
+ echo "Testarray Contents:\n";
+ $r = $r->get( 'testarray' );
+ foreach ($r as $onekey => $oneval) {
+ echo "$onekey => $oneval\n";
+ }
+?>
+--EXPECTF--
+Here comes testarray:
+ResourceBundle Object
+(
+)
+
+Here comes testbin:
+a1b2c3d4e5f67890
+
+Here comes testint:
+2
+
+Here comes teststring:
+48656c6c6f20576f726c6421
+
+Here comes testtable:
+ResourceBundle Object
+(
+)
+
+Here comes testvector:
+Array
+(
+ [0] => 1
+ [1] => 2
+ [2] => 3
+ [3] => 4
+ [4] => 5
+ [5] => 6
+ [6] => 7
+ [7] => 8
+ [8] => 9
+ [9] => 0
+)
+
+Testarray Contents:
+0 => string 1
+1 => string 2
+2 => string 3
diff --git a/ext/intl/tests/resourcebundle_locales.phpt b/ext/intl/tests/resourcebundle_locales.phpt
new file mode 100644
index 0000000..e14a7e5
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_locales.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Test ResourceBundle::getLocales
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+include "resourcebundle.inc";
+
+function ut_main() {
+ $str_res = '';
+
+ $str_res .= join("\n", ut_resourcebundle_locales(BUNDLE));
+
+ return $str_res;
+}
+
+ include_once( 'ut_common.inc' );
+ ut_run();
+?>
+--EXPECT--
+es
+root
diff --git a/ext/intl/tests/resourcebundle_null_mandatory_args.phpt b/ext/intl/tests/resourcebundle_null_mandatory_args.phpt
new file mode 100644
index 0000000..17fab6d
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_null_mandatory_args.phpt
@@ -0,0 +1,28 @@
+--TEST--
+ResourceBundle constructor bundle accepts NULL for first two arguments
+--INI--
+date.timezone=Atlantic/Azores
+--SKIPIF--
+<?php
+if (!extension_loaded('intl'))
+ die('skip intl extension not enabled');
+if (version_compare(INTL_ICU_VERSION, '4.8') < 0)
+ die('skip ICU >= 4.8 only');
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+
+$r = new ResourceBundle('en_US', NULL);
+$c = $r->get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0);
+var_dump($c);
+
+ini_set('intl.default_locale', 'pt_PT');
+$r = new ResourceBundle(NULL, NULL);
+$c = $r->get('calendar')->get('gregorian')->get('DateTimePatterns')->get(0);
+var_dump($c);
+?>
+==DONE==
+--EXPECT--
+string(14) "h:mm:ss a zzzz"
+string(12) "H:mm:ss zzzz"
+==DONE==
diff --git a/ext/intl/tests/resourcebundle_traversable.phpt b/ext/intl/tests/resourcebundle_traversable.phpt
new file mode 100644
index 0000000..1e6af7b
--- /dev/null
+++ b/ext/intl/tests/resourcebundle_traversable.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug #55610: ResourceBundle does not implement Traversable
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ include "resourcebundle.inc";
+
+ $r = new ResourceBundle( 'es', BUNDLE );
+
+ var_dump($r instanceof Traversable);
+ var_dump(iterator_to_array($r->get('testarray')));
+?>
+--EXPECTF--
+bool(true)
+array(3) {
+ [0]=>
+ string(8) "cadena 1"
+ [1]=>
+ string(8) "cadena 2"
+ [2]=>
+ string(8) "cadena 3"
+}
diff --git a/ext/intl/tests/spoofchecker_001.phpt b/ext/intl/tests/spoofchecker_001.phpt
new file mode 100644
index 0000000..7904b3a
--- /dev/null
+++ b/ext/intl/tests/spoofchecker_001.phpt
@@ -0,0 +1,23 @@
+--TEST--
+spoofchecker suspicious character checker
+--SKIPIF--
+<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?>
+--FILE--
+<?php
+
+$url = "http://www.payp\xD0\xB0l.com";
+
+$x = new Spoofchecker();
+echo "paypal with Cyrillic spoof characters\n";
+var_dump($x->isSuspicious($url));
+
+echo "certain all-uppercase Latin sequences can be spoof of Greek\n";
+var_dump($x->isSuspicious("NAPKIN PEZ"));
+var_dump($x->isSuspicious("napkin pez"));
+?>
+--EXPECTF--
+paypal with Cyrillic spoof characters
+bool(true)
+certain all-uppercase Latin sequences can be spoof of Greek
+bool(true)
+bool(false)
diff --git a/ext/intl/tests/spoofchecker_002.phpt b/ext/intl/tests/spoofchecker_002.phpt
new file mode 100644
index 0000000..d570917
--- /dev/null
+++ b/ext/intl/tests/spoofchecker_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+spoofchecker confusable tests
+--SKIPIF--
+<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?>
+--FILE--
+<?php
+
+$url = "http://www.payp\xD0\xB0l.com";
+
+$x = new Spoofchecker();
+echo "Checking if words are confusable\n";
+var_dump($x->areConfusable("hello, world", "goodbye, world"));
+var_dump($x->areConfusable("hello, world", "hello, world"));
+var_dump($x->areConfusable("hello, world", "he11o, wor1d"));
+?>
+--EXPECTF--
+Checking if words are confusable
+bool(false)
+bool(true)
+bool(true)
diff --git a/ext/intl/tests/spoofchecker_003.phpt b/ext/intl/tests/spoofchecker_003.phpt
new file mode 100644
index 0000000..0be9bfa
--- /dev/null
+++ b/ext/intl/tests/spoofchecker_003.phpt
@@ -0,0 +1,25 @@
+--TEST--
+spoofchecker with locale settings
+--SKIPIF--
+<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?>
+--FILE--
+<?php
+
+$korean = "\xED\x95\x9C" . "\xEA\xB5\xAD" . "\xEB\xA7\x90";
+
+$x = new Spoofchecker();
+echo "Is suspcious, en_US\n";
+
+$x->setAllowedLocales('en_US');
+var_dump($x->isSuspicious($korean));
+
+echo "Is suspcious, ko_KR\n";
+
+$x->setAllowedLocales('en_US, ko_KR');
+var_dump($x->isSuspicious($korean));
+?>
+--EXPECTF--
+Is suspcious, en_US
+bool(true)
+Is suspcious, ko_KR
+bool(false)
diff --git a/ext/intl/tests/spoofchecker_004.phpt b/ext/intl/tests/spoofchecker_004.phpt
new file mode 100644
index 0000000..b38c61d
--- /dev/null
+++ b/ext/intl/tests/spoofchecker_004.phpt
@@ -0,0 +1,28 @@
+--TEST--
+spoofchecker with settings changed
+--SKIPIF--
+<?php if(!extension_loaded('intl') || !class_exists("Spoofchecker")) print 'skip'; ?>
+--FILE--
+<?php
+
+$korean = "\xED\x95\x9C" . "\xEA\xB5\xAD" . "\xEB\xA7\x90";
+
+$x = new Spoofchecker();
+echo "Check with default settings\n";
+var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO"));
+var_dump($x->areConfusable("hello", "h\xD0\xB5llo"));
+
+echo "Change confusable settings\n";
+$x->setChecks(Spoofchecker::MIXED_SCRIPT_CONFUSABLE |
+ Spoofchecker::WHOLE_SCRIPT_CONFUSABLE |
+ Spoofchecker::SINGLE_SCRIPT_CONFUSABLE);
+var_dump($x->areConfusable("HELLO", "H\xD0\x95LLO"));
+var_dump($x->areConfusable("hello", "h\xD0\xB5llo"));
+?>
+--EXPECTF--
+Check with default settings
+bool(true)
+bool(true)
+Change confusable settings
+bool(false)
+bool(true)
diff --git a/ext/intl/tests/symfony_format_type_double_intl1.phpt b/ext/intl/tests/symfony_format_type_double_intl1.phpt
new file mode 100644
index 0000000..13a7ad7
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_double_intl1.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeDoubleIntl #1
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeDoubleIntl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:1:"1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeDoubleIntl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_DOUBLE);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(1) "1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_double_intl2.phpt b/ext/intl/tests/symfony_format_type_double_intl2.phpt
new file mode 100644
index 0000000..6bff7cc
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_double_intl2.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeDoubleIntl #2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeDoubleIntl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:3:"1.1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeDoubleIntl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_DOUBLE);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(3) "1.1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_double_intl3.phpt b/ext/intl/tests/symfony_format_type_double_intl3.phpt
new file mode 100644
index 0000000..3b0d576
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_double_intl3.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeDoubleIntl #3
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeDoubleIntl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:7:"SFD1.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeDoubleIntl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_DOUBLE);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(7) "SFD1.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_double_intl4.phpt b/ext/intl/tests/symfony_format_type_double_intl4.phpt
new file mode 100644
index 0000000..3476e10
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_double_intl4.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeDoubleIntl #4
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeDoubleIntl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:7:"SFD1.10";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeDoubleIntl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_DOUBLE);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(7) "SFD1.10"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl1.phpt b/ext/intl/tests/symfony_format_type_int32_intl1.phpt
new file mode 100644
index 0000000..2867b35
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl1.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #1
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+// port of Symfony's Symfony\Component\Locale\Tests\Stub\StubNumberFormatterTest#testFormatTypeInt32Intl
+
+
+// Crashes on Windows
+// Windows note: the popup '...program has stopped working'(AEDebug Popup)
+// doesn't always show if you're rapidly running this test repeatedly.
+// regardless of that, the test always crashes every time.
+// (it will show up the first time, or if you wait a while before running it again.)
+// (the popup may also be disabled, which can be done with a registry setting.)
+// you can confirm it crashed by checking the exit code OR
+// the message this test prints at the very end (expected output for pass).
+//
+// Get Exit Code
+// Linux: echo $?
+// Windows: echo %ErrorLevel%
+
+
+
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:1:"1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(1) "1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl2.phpt b/ext/intl/tests/symfony_format_type_int32_intl2.phpt
new file mode 100644
index 0000000..6a65a0a
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+// StubNumberFormatterTest#testFormatTypeInt32Intl is tested many times, each with different args.
+// there are 7 sets of args that crash PHP (and other args that don't), each of those 7 is now a separate PHPT test
+// to ensure that each of the 7 args are always tested.
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:1:"1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(1) "1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl3.phpt b/ext/intl/tests/symfony_format_type_int32_intl3.phpt
new file mode 100644
index 0000000..5e657db
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl3.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #3
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:4:{i:0;O:15:"NumberFormatter":0:{}i:1;d:2147483648;i:2;s:14:"-2,147,483,648";i:3;s:83:"->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(2147483648)
+ [2]=>
+ string(14) "-2,147,483,648"
+ [3]=>
+ string(83) "->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range."
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl4.phpt b/ext/intl/tests/symfony_format_type_int32_intl4.phpt
new file mode 100644
index 0000000..54043d9
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl4.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #4
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:7:"SFD1.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(7) "SFD1.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl5.phpt b/ext/intl/tests/symfony_format_type_int32_intl5.phpt
new file mode 100644
index 0000000..d5f78d7
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl5.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #5
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:7:"SFD1.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(7) "SFD1.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl6.phpt b/ext/intl/tests/symfony_format_type_int32_intl6.phpt
new file mode 100644
index 0000000..fa70879
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl6.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #6
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:4:{i:0;O:15:"NumberFormatter":0:{}i:1;d:2147483648;i:2;s:21:"(SFD2,147,483,648.00)";i:3;s:83:"->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(2147483648)
+ [2]=>
+ string(21) "(SFD2,147,483,648.00)"
+ [3]=>
+ string(83) "->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range."
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int32_intl7.phpt b/ext/intl/tests/symfony_format_type_int32_intl7.phpt
new file mode 100644
index 0000000..5bbe426
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int32_intl7.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt32Intl #7
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt32Intl
+$unit_test_args = unserialize('a:4:{i:0;O:15:"NumberFormatter":0:{}i:1;d:-2147483649;i:2;s:19:"SFD2,147,483,647.00";i:3;s:83:"->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range.";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt32Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT32);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(-2147483649)
+ [2]=>
+ string(19) "SFD2,147,483,647.00"
+ [3]=>
+ string(83) "->format() TYPE_INT32 formats inconsistently an integer if out of the 32 bit range."
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl1.phpt b/ext/intl/tests/symfony_format_type_int64_intl1.phpt
new file mode 100644
index 0000000..01f5820
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl1.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #1
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:1:"1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+//$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(1) "1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl2.phpt b/ext/intl/tests/symfony_format_type_int64_intl2.phpt
new file mode 100644
index 0000000..13d1cda
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl2.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #2
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:1:"1";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(1) "1"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl3.phpt b/ext/intl/tests/symfony_format_type_int64_intl3.phpt
new file mode 100644
index 0000000..a7c80b3
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl3.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #3
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:2147483648;i:2;s:13:"2,147,483,648";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(2147483648)
+ [2]=>
+ string(13) "2,147,483,648"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl4.phpt b/ext/intl/tests/symfony_format_type_int64_intl4.phpt
new file mode 100644
index 0000000..f1a0801
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl4.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #4
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:-2147483649;i:2;s:14:"-2,147,483,649";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(-2147483649)
+ [2]=>
+ string(14) "-2,147,483,649"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl5.phpt b/ext/intl/tests/symfony_format_type_int64_intl5.phpt
new file mode 100644
index 0000000..dad8735
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl5.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #5
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;i:1;i:2;s:7:"SFD1.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ int(1)
+ [2]=>
+ string(7) "SFD1.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl6.phpt b/ext/intl/tests/symfony_format_type_int64_intl6.phpt
new file mode 100644
index 0000000..f038cbd
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl6.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #6
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:1.1000000000000001;i:2;s:7:"SFD1.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(1.1)
+ [2]=>
+ string(7) "SFD1.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl7.phpt b/ext/intl/tests/symfony_format_type_int64_intl7.phpt
new file mode 100644
index 0000000..9c8853c
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl7.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #7
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:2147483648;i:2;s:19:"SFD2,147,483,648.00";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(2147483648)
+ [2]=>
+ string(19) "SFD2,147,483,648.00"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/symfony_format_type_int64_intl8.phpt b/ext/intl/tests/symfony_format_type_int64_intl8.phpt
new file mode 100644
index 0000000..5052497
--- /dev/null
+++ b/ext/intl/tests/symfony_format_type_int64_intl8.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Symfony StubNumberFormatterTest#testFormatTypeInt64Intl #8
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+
+// PHP Unit's code to unserialize data passed as args to #testFormatTypeInt64Intl
+$unit_test_args = unserialize('a:3:{i:0;O:15:"NumberFormatter":0:{}i:1;d:-2147483649;i:2;s:21:"(SFD2,147,483,649.00)";}');
+
+var_dump($unit_test_args);
+
+// execute the code from #testFormatTypeInt64Intl
+$unit_test_args[0]->format($unit_test_args[1], \NumberFormatter::TYPE_INT64);
+
+echo "== didn't crash ==".PHP_EOL;
+
+?>
+--EXPECT--
+array(3) {
+ [0]=>
+ object(NumberFormatter)#1 (0) {
+ }
+ [1]=>
+ float(-2147483649)
+ [2]=>
+ string(21) "(SFD2,147,483,649.00)"
+}
+== didn't crash ==
diff --git a/ext/intl/tests/transliterator_clone.phpt b/ext/intl/tests/transliterator_clone.phpt
new file mode 100644
index 0000000..23d569b
--- /dev/null
+++ b/ext/intl/tests/transliterator_clone.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Transliterator clone handler
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$str = "a U+4E07";
+
+$t = Transliterator::create("hex-any");
+echo $t->id, ": ", $t->transliterate($str), "\n";
+
+$u = clone $t;
+echo $u->id, ": ", $u->transliterate($str), "\n";
+
+echo "Done.\n";
+
+--EXPECT--
+hex-any: a 万
+hex-any: a 万
+Done.
diff --git a/ext/intl/tests/transliterator_create_basic.phpt b/ext/intl/tests/transliterator_create_basic.phpt
new file mode 100644
index 0000000..4de032d
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_basic.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Transliterator::create (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$t = Transliterator::create("any-latin");
+echo $t->id,"\n";
+
+$t = transliterator_create("any-latin");
+echo $t->id,"\n";
+
+echo "Done.\n";
+
+--EXPECT--
+any-latin
+any-latin
+Done.
+
diff --git a/ext/intl/tests/transliterator_create_error.phpt b/ext/intl/tests/transliterator_create_error.phpt
new file mode 100644
index 0000000..31aef68
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_error.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Transliterator::create (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+Transliterator::create("inexistant id");
+echo intl_get_error_message(), "\n";
+Transliterator::create("bad UTF-8 \x8F");
+echo intl_get_error_message(), "\n";
+
+echo "Done.\n";
+--EXPECTF--
+Warning: Transliterator::create(): transliterator_create: unable to open ICU transliterator with id "inexistant id" in %s on line %d
+transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID
+
+Warning: Transliterator::create(): String conversion of id to UTF-16 failed in %s on line %d
+String conversion of id to UTF-16 failed: U_INVALID_CHAR_FOUND
+Done.
diff --git a/ext/intl/tests/transliterator_create_from_rule_basic.phpt b/ext/intl/tests/transliterator_create_from_rule_basic.phpt
new file mode 100644
index 0000000..eb8d5da
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_from_rule_basic.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Transliterator::createFromRules (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+
+$rules = <<<RULES
+α <> y;
+\`\` } a > “;
+RULES;
+
+$t = Transliterator::createFromRules($rules);
+echo $t->id,"\n";
+
+echo $t->transliterate("``akk ``bkk ``aooy"),"\n";
+
+$u = transliterator_create_from_rules($rules, Transliterator::REVERSE);
+
+echo $u->transliterate("``akk ``bkk ``aooy"), "\n";
+
+echo "Done.\n";
+--EXPECT--
+RulesTransPHP
+“akk ``bkk “aooy
+``akk ``bkk ``aooα
+Done.
diff --git a/ext/intl/tests/transliterator_create_from_rule_error.phpt b/ext/intl/tests/transliterator_create_from_rule_error.phpt
new file mode 100644
index 0000000..cad73ab
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_from_rule_error.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Transliterator::createFromRules (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+
+$t = Transliterator::createFromRules();
+echo intl_get_error_message(),"\n";
+
+$t = Transliterator::createFromRules("a","b");
+echo intl_get_error_message(),"\n";
+
+$t = Transliterator::createFromRules("\x8Fss");
+echo intl_get_error_message(),"\n";
+
+$rules = <<<RULES
+\`\` } a > “;
+\`\` } a > b;
+RULES;
+
+$t = Transliterator::createFromRules($rules);
+echo intl_get_error_message(),"\n";
+
+$rules = <<<RULES
+ffff
+RULES;
+
+$t = Transliterator::createFromRules($rules);
+echo intl_get_error_message(),"\n";
+echo "Done.\n";
+
+--EXPECTF--
+Warning: Transliterator::createFromRules() expects at least 1 parameter, 0 given in %s on line %d
+
+Warning: Transliterator::createFromRules(): transliterator_create_from_rules: bad arguments in %s on line %d
+transliterator_create_from_rules: bad arguments: U_ILLEGAL_ARGUMENT_ERROR
+
+Warning: Transliterator::createFromRules() expects parameter 2 to be long, string given in %s on line %d
+
+Warning: Transliterator::createFromRules(): transliterator_create_from_rules: bad arguments in %s on line %d
+transliterator_create_from_rules: bad arguments: U_ILLEGAL_ARGUMENT_ERROR
+
+Warning: Transliterator::createFromRules(): String conversion of rules to UTF-16 failed in %s on line %d
+String conversion of rules to UTF-16 failed: U_INVALID_CHAR_FOUND
+
+Warning: Transliterator::createFromRules(): transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error after "{'``'}a > “;", before or at "{'``'}a > b;") in %s on line %d
+transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error after "{'``'}a > “;", before or at "{'``'}a > b;"): U_RULE_MASK_ERROR
+
+Warning: Transliterator::createFromRules(): transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error at offset 0, before or at "ffff") in %s on line %d
+transliterator_create_from_rules: unable to create ICU transliterator from rules (parse error at offset 0, before or at "ffff"): U_MISSING_OPERATOR
+Done.
diff --git a/ext/intl/tests/transliterator_create_inverse_basic.phpt b/ext/intl/tests/transliterator_create_inverse_basic.phpt
new file mode 100644
index 0000000..3241403
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_inverse_basic.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Transliterator::createInverse (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+
+$tr = Transliterator::create("Katakana-Latin");
+$orstr = "オーシャンビュー";
+$new_str = $tr->transliterate($orstr);
+
+$revtr = $tr->createInverse();
+$recovstr = $revtr->transliterate($new_str);
+
+$revtr2 = transliterator_create_inverse($tr);
+$recovstr2 = $revtr2->transliterate($new_str);
+
+echo $orstr,"\n";
+echo $new_str,"\n";
+echo $recovstr,"\n";
+
+var_dump(($orstr == $recovstr) == $recovstr2);
+
+echo "Done.\n";
+--EXPECT--
+オーシャンビュー
+ōshanbyū
+オーシャンビュー
+bool(true)
+Done.
diff --git a/ext/intl/tests/transliterator_create_inverse_error.phpt b/ext/intl/tests/transliterator_create_inverse_error.phpt
new file mode 100644
index 0000000..92141c9
--- /dev/null
+++ b/ext/intl/tests/transliterator_create_inverse_error.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Transliterator::createInverse (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+
+$tr = Transliterator::create("Katakana-Latin");
+$tr->createInverse(array());
+
+$tr = Transliterator::create("Katakana-Latin");
+transliterator_create_inverse("jj");
+
+--EXPECTF--
+Warning: Transliterator::createInverse() expects exactly 0 parameters, 1 given in %s on line %d
+
+Warning: Transliterator::createInverse(): transliterator_create_inverse: bad arguments in %s on line %d
+
+Catchable fatal error: Argument 1 passed to transliterator_create_inverse() must be an instance of Transliterator, string given in %s on line %d
diff --git a/ext/intl/tests/transliterator_get_error_code_basic.phpt b/ext/intl/tests/transliterator_get_error_code_basic.phpt
new file mode 100644
index 0000000..3d16a8a
--- /dev/null
+++ b/ext/intl/tests/transliterator_get_error_code_basic.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Transliterator::getErrorCode (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex");
+var_dump($t->transliterate("\x8F"));
+echo transliterator_get_error_code($t), "\n";
+
+echo $t->getErrorCode(), "\n";
+
+var_dump($t->transliterate(""));
+echo $t->getErrorCode(), "\n";
+
+echo "Done.\n";
+--EXPECTF--
+Warning: Transliterator::transliterate(): String conversion of string to UTF-16 failed in %s on line %d
+bool(false)
+10
+10
+string(0) ""
+0
+Done.
diff --git a/ext/intl/tests/transliterator_get_error_code_error.phpt b/ext/intl/tests/transliterator_get_error_code_error.phpt
new file mode 100644
index 0000000..94daa12
--- /dev/null
+++ b/ext/intl/tests/transliterator_get_error_code_error.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Transliterator::getErrorCode (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex");
+echo transliterator_get_error_code(), "\n";
+echo $t->getErrorCode(null), "\n";
+echo transliterator_get_error_code(array()), "\n";
+
+--EXPECTF--
+Warning: transliterator_get_error_code() expects exactly 1 parameter, 0 given in %s on line %d
+
+Warning: transliterator_get_error_code(): transliterator_get_error_code: unable to parse input params in %s on line %d
+
+
+Warning: Transliterator::getErrorCode() expects exactly 0 parameters, 1 given in %s on line %d
+
+Warning: Transliterator::getErrorCode(): transliterator_get_error_code: unable to parse input params in %s on line %d
+
+
+Catchable fatal error: Argument 1 passed to transliterator_get_error_code() must be an instance of Transliterator, array given in %s on line %d
diff --git a/ext/intl/tests/transliterator_get_error_message_basic.phpt b/ext/intl/tests/transliterator_get_error_message_basic.phpt
new file mode 100644
index 0000000..4e91853
--- /dev/null
+++ b/ext/intl/tests/transliterator_get_error_message_basic.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Transliterator::getErrorMessage (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex");
+var_dump($t->transliterate("\x8F"));
+echo transliterator_get_error_message($t), "\n";
+
+echo $t->getErrorMessage(), "\n";
+
+var_dump($t->transliterate(""));
+echo $t->getErrorMessage(), "\n";
+
+echo "Done.\n";
+--EXPECTF--
+Warning: Transliterator::transliterate(): String conversion of string to UTF-16 failed in %s on line %d
+bool(false)
+String conversion of string to UTF-16 failed: U_INVALID_CHAR_FOUND
+String conversion of string to UTF-16 failed: U_INVALID_CHAR_FOUND
+string(0) ""
+U_ZERO_ERROR
+Done.
diff --git a/ext/intl/tests/transliterator_get_error_message_error.phpt b/ext/intl/tests/transliterator_get_error_message_error.phpt
new file mode 100644
index 0000000..4ab2104
--- /dev/null
+++ b/ext/intl/tests/transliterator_get_error_message_error.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Transliterator::getErrorMessage (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+$t = Transliterator::create("[\p{Bidi_Mirrored}] Hex");
+echo transliterator_get_error_message(), "\n";
+echo $t->getErrorMessage(null), "\n";
+echo transliterator_get_error_message(array()), "\n";
+
+--EXPECTF--
+Warning: transliterator_get_error_message() expects exactly 1 parameter, 0 given in %s on line %d
+
+Warning: transliterator_get_error_message(): transliterator_get_error_message: unable to parse input params in %s on line %d
+
+
+Warning: Transliterator::getErrorMessage() expects exactly 0 parameters, 1 given in %s on line %d
+
+Warning: Transliterator::getErrorMessage(): transliterator_get_error_message: unable to parse input params in %s on line %d
+
+
+Catchable fatal error: Argument 1 passed to transliterator_get_error_message() must be an instance of Transliterator, array given in %s on line %d
diff --git a/ext/intl/tests/transliterator_list_ids_basic.phpt b/ext/intl/tests/transliterator_list_ids_basic.phpt
new file mode 100644
index 0000000..af65b58
--- /dev/null
+++ b/ext/intl/tests/transliterator_list_ids_basic.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Transliterator::listIDs (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+var_dump(count(transliterator_list_ids()) > 100);
+var_dump(count(Transliterator::listIDs()) > 100);
+
+echo "Done.\n";
+--EXPECT--
+bool(true)
+bool(true)
+Done.
diff --git a/ext/intl/tests/transliterator_list_ids_error.phpt b/ext/intl/tests/transliterator_list_ids_error.phpt
new file mode 100644
index 0000000..d1066a8
--- /dev/null
+++ b/ext/intl/tests/transliterator_list_ids_error.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Transliterator::listIDs (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+var_dump(transliterator_list_ids(array()));
+
+echo "Done.\n";
+
+--EXPECTF--
+Warning: transliterator_list_ids() expects exactly 0 parameters, 1 given in %s on line %d
+
+Warning: transliterator_list_ids(): transliterator_list_ids: bad arguments in %s on line %d
+bool(false)
+Done.
diff --git a/ext/intl/tests/transliterator_property_id.phpt b/ext/intl/tests/transliterator_property_id.phpt
new file mode 100644
index 0000000..b5337b6
--- /dev/null
+++ b/ext/intl/tests/transliterator_property_id.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Transliterator - "id" property
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+$tr = Transliterator::create("Katakana-Latin");
+echo $tr->id, "\n";
+$revtr = $tr->createInverse();
+echo $revtr->id, "\n";
+var_dump($revtr);
+
+echo "Done.\n";
+--EXPECTF--
+Katakana-Latin
+Latin-Katakana
+object(Transliterator)#%d (%d) {
+ ["id"]=>
+ string(%d) "Latin-Katakana"
+}
+Done.
diff --git a/ext/intl/tests/transliterator_transliterate_basic.phpt b/ext/intl/tests/transliterator_transliterate_basic.phpt
new file mode 100644
index 0000000..f117834
--- /dev/null
+++ b/ext/intl/tests/transliterator_transliterate_basic.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Transliterator::transliterate (basic)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+$t = transliterator_create("Latin; Title");
+$s = "Κοντογιαννάτος, Βασίλης";
+echo $t->transliterate($s),"\n";
+echo transliterator_transliterate($t, $s),"\n";
+echo $t->transliterate($s, 3),"\n";
+echo $t->transliterate($s, 3, 4),"\n";
+
+echo "Done.\n";
+--EXPECT--
+Kontogiannátos, Basílēs
+Kontogiannátos, Basílēs
+ΚονTogiannátos, Basílēs
+ΚονTογιαννάτος, Βασίλης
+Done.
diff --git a/ext/intl/tests/transliterator_transliterate_error.phpt b/ext/intl/tests/transliterator_transliterate_error.phpt
new file mode 100644
index 0000000..cdddcfb
--- /dev/null
+++ b/ext/intl/tests/transliterator_transliterate_error.phpt
@@ -0,0 +1,60 @@
+--TEST--
+Transliterator::transliterate (error)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+
+$tr = Transliterator::create("latin");
+
+//Arguments
+var_dump(transliterator_transliterate());
+var_dump(transliterator_transliterate($tr,array()));
+var_dump(transliterator_transliterate($tr,"str",7));
+var_dump(transliterator_transliterate($tr,"str",7,6));
+var_dump(transliterator_transliterate($tr,"str",2,-1,"extra"));
+
+//Arguments
+var_dump($tr->transliterate());
+var_dump($tr->transliterate(array()));
+
+//bad UTF-8
+transliterator_transliterate($tr, "\x80\x03");
+
+echo "Done.\n";
+--EXPECTF--
+Warning: transliterator_transliterate() expects at least 2 parameters, 0 given in %s on line %d
+
+Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d
+bool(false)
+
+Warning: transliterator_transliterate() expects parameter 2 to be string, array given in %s on line %d
+
+Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d
+bool(false)
+
+Warning: transliterator_transliterate(): transliterator_transliterate: Neither "start" nor the "end" arguments can exceed the number of UTF-16 code units (in this case, 3) in %s on line %d
+bool(false)
+
+Warning: transliterator_transliterate(): transliterator_transliterate: "start" argument should be non-negative and not bigger than "end" (if defined) in %s on line %d
+bool(false)
+
+Warning: transliterator_transliterate() expects at most 4 parameters, 5 given in %s on line %d
+
+Warning: transliterator_transliterate(): transliterator_transliterate: bad arguments in %s on line %d
+bool(false)
+
+Warning: Transliterator::transliterate() expects at least 1 parameter, 0 given in %s on line %d
+
+Warning: Transliterator::transliterate(): transliterator_transliterate: bad arguments in %s on line %d
+bool(false)
+
+Warning: Transliterator::transliterate() expects parameter 1 to be string, array given in %s on line %d
+
+Warning: Transliterator::transliterate(): transliterator_transliterate: bad arguments in %s on line %d
+bool(false)
+
+Warning: transliterator_transliterate(): String conversion of string to UTF-16 failed in %s on line %d
+Done.
diff --git a/ext/intl/tests/transliterator_transliterate_variant1.phpt b/ext/intl/tests/transliterator_transliterate_variant1.phpt
new file mode 100644
index 0000000..fc77a4e
--- /dev/null
+++ b/ext/intl/tests/transliterator_transliterate_variant1.phpt
@@ -0,0 +1,37 @@
+--TEST--
+transliterator_transliterate (variant 1, non-transliterator 1st arg)
+--SKIPIF--
+<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?>
+--FILE--
+<?php
+ini_set("intl.error_level", E_WARNING);
+//exec('pause');
+$str = " o";
+echo transliterator_transliterate("[\p{White_Space}] hex", $str), "\n";
+
+echo transliterator_transliterate("\x8F", $str), "\n";
+echo intl_get_error_message(), "\n";
+
+class A {
+function __toString() { return "inexistant id"; }
+}
+
+echo transliterator_transliterate(new A(), $str), "\n";
+echo intl_get_error_message(), "\n";
+
+echo "Done.\n";
+--EXPECTF--
+\u0020o
+
+Warning: transliterator_transliterate(): String conversion of id to UTF-16 failed in %s on line %d
+
+Warning: transliterator_transliterate(): Could not create transliterator with ID %s
+
+String conversion of id to UTF-16 failed: U_INVALID_CHAR_FOUND
+
+Warning: transliterator_transliterate(): transliterator_create: unable to open ICU transliterator with id "inexistant id" in %s on line %d
+
+Warning: transliterator_transliterate(): Could not create transliterator with ID "inexistant id" (transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID) in %s on line %d
+
+transliterator_create: unable to open ICU transliterator with id "inexistant id": U_INVALID_ID
+Done.
diff --git a/ext/intl/tests/ut_common.inc b/ext/intl/tests/ut_common.inc
new file mode 100644
index 0000000..09be22b
--- /dev/null
+++ b/ext/intl/tests/ut_common.inc
@@ -0,0 +1,416 @@
+<?php
+/*
+ * Run unit test in OO- and in procedural mode.
+ * Then compare the outputs.
+ * It they're equal then show one of them.
+ * Otherwise indicate an error.
+ */
+function ut_run()
+{
+ // Run unit test in OO mode.
+ $GLOBALS['oo-mode'] = true;
+ $oo_result = ut_main();
+
+ // Run unit test in procedural mode.
+ $GLOBALS['oo-mode'] = false;
+ $proc_result = ut_main();
+
+ // Show error if the APIs produce different results.
+ if( $proc_result !== $oo_result )
+ {
+ echo "ERROR: OO- and procedural APIs produce different results!\n";
+ echo "OO API output:\n";
+ echo str_repeat( '=', 78 ) . "\n";
+ echo $oo_result;
+ echo str_repeat( '=', 78 ) . "\n";
+ echo "procedural API output:\n";
+ echo str_repeat( '=', 78 ) . "\n";
+ echo $proc_result;
+ echo str_repeat( '=', 78 ) . "\n";
+ return;
+ }
+
+ // Else, if the results are equal, show one of them.
+ echo $proc_result;
+}
+
+function dump( $val )
+{
+ return var_export( $val, true );
+}
+
+/*
+ * Wrappers around Collator methods to run them in either OO- or procedural mode.
+ */
+
+function ut_coll_create( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Collator::create( $locale ) : collator_create( $locale );
+}
+function ut_coll_compare( $coll, $str1, $str2 )
+{
+ return $GLOBALS['oo-mode'] ? $coll->compare( $str1, $str2 ) : collator_compare( $coll, $str1, $str2 );
+}
+function ut_coll_sort( $coll, &$arr, $sort_flag = Collator::SORT_REGULAR )
+{
+ return $GLOBALS['oo-mode'] ? $coll->sort( $arr, $sort_flag ) : collator_sort( $coll, $arr, $sort_flag );
+}
+function ut_coll_sort_with_sort_keys( $coll, &$arr )
+{
+ return $GLOBALS['oo-mode'] ? $coll->sortWithSortKeys( $arr ) : collator_sort_with_sort_keys( $coll, $arr );
+}
+function ut_coll_get_sort_key( $coll, $str )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getSortKey( $str ) : collator_get_sort_key( $coll, $str );
+}
+function ut_coll_asort( $coll, &$arr, $sort_flag = Collator::SORT_REGULAR )
+{
+ return $GLOBALS['oo-mode'] ? $coll->asort( $arr, $sort_flag ) : collator_asort( $coll, $arr, $sort_flag );
+}
+function ut_coll_get_locale( $coll, $type )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getLocale( $type ) : collator_get_locale( $coll, $type );
+}
+function ut_coll_get_display_name( $obj_loc, $disp_loc )
+{
+ return $GLOBALS['oo-mode'] ? Collator::getDisplayName( $obj_loc, $disp_loc ) : collator_get_display_name( $obj_loc, $disp_loc );
+}
+function ut_coll_get_available_locales()
+{
+ return $GLOBALS['oo-mode'] ? Collator::getAvailableLocales() : collator_get_available_locales();
+}
+function ut_coll_get_attribute( $coll, $attr )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getAttribute( $attr ) : collator_get_attribute( $coll, $attr );
+}
+function ut_coll_get_strength( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getStrength() : collator_get_strength( $coll );
+}
+function ut_coll_set_strength( $coll, $strength )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setStrength( $strength ) : collator_set_strength( $coll, $strength );
+}
+function ut_coll_set_attribute( $coll, $attr, $val )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setAttribute( $attr, $val ) : collator_set_attribute( $coll, $attr, $val );
+}
+function ut_coll_get_variable_top( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getVariableTop() : collator_get_variable_top( $coll );
+}
+function ut_coll_set_variable_top( $coll, $var_top )
+{
+ return $GLOBALS['oo-mode'] ? $coll->setVariableTop( $var_top ) : collator_set_variable_top( $coll, $var_top );
+}
+function ut_coll_restore_variable_top( $coll, $var_top )
+{
+ return $GLOBALS['oo-mode'] ? $coll->restoreVariableTop( $var_top ) : collator_restore_variable_top( $coll, $var_top );
+}
+function ut_coll_get_error_code( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getErrorCode() : collator_get_error_code( $coll );
+}
+function ut_coll_get_error_message( $coll )
+{
+ return $GLOBALS['oo-mode'] ? $coll->getErrorMessage() : collator_get_error_message( $coll );
+}
+function ut_coll_get_default()
+{
+ return $GLOBALS['oo-mode'] ? Collator::getDefault() : collator_get_default();
+}
+function ut_coll_set_default( $coll )
+{
+ return $GLOBALS['oo-mode'] ? Collator::setDefault( $coll ) : collator_set_default( $coll );
+}
+
+/*
+ * Wrappers around NumberFormatter methods to run them in either OO- or procedural mode.
+ */
+
+// FIXME: incomplete list
+
+function ut_nfmt_create( $locale, $style, $pattern = null )
+{
+ return $GLOBALS['oo-mode'] ? new NumberFormatter( $locale, $style, $pattern ) : numfmt_create( $locale, $style, $pattern );
+}
+function ut_nfmt_format( $fmt, $number, $type = null )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $number, $type ) : numfmt_format( $fmt, $number, $type );
+}
+function ut_nfmt_parse( $fmt, $string, $type = NumberFormatter::TYPE_DOUBLE, &$position = null )
+{
+ if(is_null($position)) {
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string, $type ) : numfmt_parse( $fmt, $string, $type );
+ } else {
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string, $type, $position ) : numfmt_parse( $fmt, $string, $type, $position );
+ }
+}
+function ut_nfmt_format_currency( $fmt, $number, $currency )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->formatCurrency( $number, $currency ) : numfmt_format_currency( $fmt, $number, $currency );
+}
+function ut_nfmt_parse_currency( $fmt, $string, &$currency, &$position = null )
+{
+ if(is_null($position)) {
+ return $GLOBALS['oo-mode'] ? $fmt->parseCurrency( $string, $currency ) : numfmt_parse_currency( $fmt, $string, $currency );
+ } else {
+ return $GLOBALS['oo-mode'] ? $fmt->parseCurrency( $string, $currency, $position ) : numfmt_parse_currency( $fmt, $string, $currency, $position );
+ }
+}
+function ut_nfmt_set_attribute( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setAttribute( $attribute, $value ) : numfmt_set_attribute( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_text_attribute( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setTextAttribute( $attribute, $value ) : numfmt_set_text_attribute( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_symbol( $fmt, $attribute, $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setSymbol( $attribute, $value ) : numfmt_set_symbol( $fmt, $attribute, $value );
+}
+function ut_nfmt_set_pattern( $fmt, $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : numfmt_set_pattern( $fmt, $pattern );
+}
+function ut_nfmt_get_attribute( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getAttribute( $attribute ) : numfmt_get_attribute( $fmt, $attribute );
+}
+function ut_nfmt_get_text_attribute( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTextAttribute( $attribute ) : numfmt_get_text_attribute( $fmt, $attribute );
+}
+function ut_nfmt_get_symbol( $fmt, $attribute )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getSymbol( $attribute ) : numfmt_get_symbol( $fmt, $attribute );
+}
+function ut_nfmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : numfmt_get_pattern( $fmt );
+}
+function ut_nfmt_get_locale( $fmt, $type = 0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale( $type ) : numfmt_get_locale( $fmt, $type );
+}
+function ut_nfmt_get_error_code( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorCode() : numfmt_get_error_code( $fmt );
+}
+function ut_nfmt_get_error_message( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorMessage() : numfmt_get_error_message( $fmt );
+}
+
+function ut_norm_normalize( $str, $form )
+{
+ return $GLOBALS['oo-mode'] ? Normalizer::normalize( $str, $form ) : normalizer_normalize( $str, $form );
+}
+function ut_norm_is_normalized( $str, $form )
+{
+ return $GLOBALS['oo-mode'] ? Normalizer::isNormalized( $str, $form ) : normalizer_is_normalized( $str, $form );
+}
+
+/*
+ * Wrappers around Collator methods to run them in either OO- or procedural mode.
+ */
+
+function ut_loc_get_default( )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDefault( ) : locale_get_default();
+}
+function ut_loc_set_default( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::setDefault( $locale ) : locale_set_default( $locale );
+}
+function ut_loc_get_primary_language( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getPrimaryLanguage( $locale ) : locale_get_primary_language( $locale );
+}
+function ut_loc_get_script( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getScript( $locale ) : locale_get_script( $locale );
+}
+function ut_loc_get_region( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getRegion( $locale ) : locale_get_region( $locale );
+}
+function ut_loc_get_keywords( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getKeywords( $locale ) : locale_get_keywords( $locale );
+}
+function ut_loc_get_display_name( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayName( $locale , $dispLocale ) : locale_get_display_name( $locale , $dispLocale );
+}
+function ut_loc_get_display_language( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayLanguage( $locale , $dispLocale ) : locale_get_display_language( $locale , $dispLocale );
+}
+function ut_loc_get_display_script( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayScript( $locale , $dispLocale ) : locale_get_display_script( $locale , $dispLocale );
+}
+function ut_loc_get_display_region( $locale, $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayRegion( $locale, $dispLocale ) : locale_get_display_region( $locale, $dispLocale );
+}
+function ut_loc_get_display_variant( $locale , $dispLocale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getDisplayVariant( $locale , $dispLocale ) : locale_get_display_variant( $locale, $dispLocale );
+}
+function ut_loc_locale_compose( $loc_parts_arr )
+{
+ return $GLOBALS['oo-mode'] ? Locale::composeLocale( $loc_parts_arr ) : locale_compose( $loc_parts_arr );
+}
+function ut_loc_locale_parse( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::parseLocale( $locale ) : locale_parse($locale );
+}
+function ut_loc_locale_get_all_variants( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::getAllVariants( $locale ) : locale_get_all_variants( $locale );
+}
+function ut_loc_locale_filter_matches( $lang_tag,$loc_range ,$isCanonical)
+{
+ return $GLOBALS['oo-mode'] ? Locale::filterMatches( $lang_tag,$loc_range ,$isCanonical) : locale_filter_matches( $lang_tag,$loc_range ,$isCanonical);
+}
+function ut_loc_canonicalize( $locale )
+{
+ return $GLOBALS['oo-mode'] ? Locale::canonicalize( $locale ) : locale_canonicalize( $locale );
+}
+function ut_loc_locale_lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc)
+{
+ return $GLOBALS['oo-mode'] ? Locale::lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc ) : locale_lookup( $lang_tag_arr,$loc_range,$isCanonical,$default_loc );
+}
+function ut_loc_accept_http($http) {
+ return $GLOBALS['oo-mode'] ? Locale::acceptFromHttp($http):locale_accept_from_http($http);
+}
+/* MessageFormatter functions */
+function ut_msgfmt_create( $locale, $pattern)
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::create( $locale, $pattern ) : msgfmt_create( $locale, $pattern );
+}
+function ut_msgfmt_format( $fmt, $args )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $args ) : msgfmt_format( $fmt, $args);
+}
+function ut_msgfmt_parse( $fmt, $string)
+{
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $string) : msgfmt_parse( $fmt, $string);
+}
+function ut_msgfmt_format_message( $locale, $pattern, $args )
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::formatMessage( $locale, $pattern, $args ) : msgfmt_format_message( $locale, $pattern, $args );
+}
+function ut_msgfmt_parse_message( $locale, $pattern, $string )
+{
+ return $GLOBALS['oo-mode'] ? MessageFormatter::parseMessage( $locale, $pattern, $string ) : msgfmt_parse_message( $locale, $pattern, $string );
+}
+function ut_msgfmt_set_pattern( $fmt, $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : msgfmt_set_pattern( $fmt, $pattern );
+}
+function ut_msgfmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : msgfmt_get_pattern( $fmt );
+}
+function ut_msgfmt_get_locale( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale( ) : msgfmt_get_locale( $fmt );
+}
+function ut_msgfmt_get_error_code( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorCode() : msgfmt_get_error_code( $fmt );
+}
+function ut_msgfmt_get_error_message( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getErrorMessage() : msgfmt_get_error_message( $fmt );
+}
+/* IntlDateFormatter functions */
+function ut_datefmt_create( $locale, $datetype, $timetype, $timezone = null, $calendar = null ,$pattern = null)
+{
+ return $GLOBALS['oo-mode'] ? datefmt_create( $locale, $datetype, $timetype, $timezone, $calendar ,$pattern ) : datefmt_create( $locale, $datetype, $timetype, $timezone, $calendar ,$pattern);
+}
+function ut_datefmt_get_datetype( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getDateType( ) : datefmt_get_datetype( $fmt );
+}
+function ut_datefmt_get_timetype( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTimeType( ) : datefmt_get_timetype( $fmt );
+}
+function ut_datefmt_get_calendar( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getCalendar( ) : datefmt_get_calendar( $fmt );
+}
+function ut_datefmt_set_calendar( $fmt ,$calendar )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setCalendar( $calendar ) : datefmt_set_calendar( $fmt , $calendar );
+}
+function ut_datefmt_get_timezone_id( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getTimeZoneId( ) : datefmt_get_timezone_id( $fmt );
+}
+function ut_datefmt_set_timezone_id( $fmt ,$timezone_id )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setTimeZoneId( $timezone_id ) : datefmt_set_timezone_id( $fmt ,$timezone_id);
+}
+function ut_datefmt_get_pattern( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getPattern() : datefmt_get_pattern( $fmt );
+}
+function ut_datefmt_set_pattern( $fmt , $pattern )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setPattern( $pattern ) : datefmt_set_pattern( $fmt , $pattern);
+}
+function ut_datefmt_get_locale( $fmt ,$type=ULOC_ACTUAL_LOCALE)
+{
+ return $GLOBALS['oo-mode'] ? $fmt->getLocale($type ) : datefmt_get_locale( $fmt ,$type);
+}
+function ut_datefmt_is_lenient( $fmt )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->isLenient() : datefmt_is_lenient( $fmt );
+}
+function ut_datefmt_set_lenient( $fmt , $lenient )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->setLenient( $lenient ) : datefmt_set_lenient( $fmt , $lenient);
+}
+function ut_datefmt_format( $fmt , $value )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->format( $value ) : datefmt_format( $fmt , $value);
+}
+function ut_datefmt_parse( $fmt , $value , &$parse_pos=0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->parse( $value ,$parse_pos ) : datefmt_parse( $fmt , $value,$parse_pos);
+}
+function ut_datefmt_localtime( $fmt , $value , &$parse_pos=0 )
+{
+ return $GLOBALS['oo-mode'] ? $fmt->localtime( $value , $parse_pos ) : datefmt_localtime( $fmt , $value , $parse_pos );
+}
+
+function ut_resourcebundle_create( $locale, $bundle, $fallback=true )
+{
+ return $GLOBALS['oo-mode'] ? new ResourceBundle($locale, $bundle, $fallback): resourcebundle_create($locale, $bundle, $fallback);
+}
+function ut_resourcebundle_count($bundle )
+{
+ return $GLOBALS['oo-mode'] ? $bundle->count():resourcebundle_count($bundle);
+}
+function ut_resourcebundle_locales($bundle )
+{
+ return $GLOBALS['oo-mode'] ? ResourceBundle::getLocales($bundle):resourcebundle_locales($bundle);
+}
+function ut_resourcebundle_get($bundle, $idx )
+{
+ return $GLOBALS['oo-mode'] ? $bundle->get($idx):resourcebundle_get($bundle, $idx);
+}
+function ut_resourcebundle_get_error_code($bundle )
+{
+ return $GLOBALS['oo-mode'] ? $bundle->getErrorCode():resourcebundle_get_error_code($bundle);
+}
+function ut_resourcebundle_get_error_message($bundle )
+{
+ return $GLOBALS['oo-mode'] ? $bundle->getErrorMessage():resourcebundle_get_error_message($bundle);
+}
diff --git a/ext/intl/transliterator/transliterator.c b/ext/intl/transliterator/transliterator.c
new file mode 100644
index 0000000..75c9eaa
--- /dev/null
+++ b/ext/intl/transliterator/transliterator.c
@@ -0,0 +1,138 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "transliterator_class.h"
+#include "transliterator.h"
+#include "intl_convert.h"
+
+#include <unicode/ustring.h>
+
+/* {{{ transliterator_register_constants
+ * Register constants common for both (OO and procedural) APIs.
+ */
+void transliterator_register_constants( INIT_FUNC_ARGS )
+{
+ if( !Transliterator_ce_ptr )
+ {
+ zend_error( E_ERROR, "Transliterator class not defined" );
+ return;
+ }
+
+ #define TRANSLITERATOR_EXPOSE_CONST( x ) REGISTER_LONG_CONSTANT( #x, x, CONST_CS )
+ #define TRANSLITERATOR_EXPOSE_CLASS_CONST( x ) zend_declare_class_constant_long( Transliterator_ce_ptr, ZEND_STRS( #x ) - 1, TRANSLITERATOR_##x TSRMLS_CC );
+ #define TRANSLITERATOR_EXPOSE_CUSTOM_CLASS_CONST( name, value ) zend_declare_class_constant_long( Transliterator_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC );*/
+
+ /* Normalization form constants */
+ TRANSLITERATOR_EXPOSE_CLASS_CONST( FORWARD );
+ TRANSLITERATOR_EXPOSE_CLASS_CONST( REVERSE );
+
+ #undef NORMALIZER_EXPOSE_CUSTOM_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_CLASS_CONST
+ #undef NORMALIZER_EXPOSE_CONST
+}
+/* }}} */
+
+/* {{{ transliterator_parse_error_to_string
+ * Transforms parse errors in strings.
+ */
+smart_str transliterator_parse_error_to_string( UParseError* pe )
+{
+ smart_str ret = {0};
+ char *buf;
+ int u8len;
+ UErrorCode status;
+ int any = 0;
+
+ assert( pe != NULL );
+
+ smart_str_appends( &ret, "parse error " );
+ if( pe->line > 0 )
+ {
+ smart_str_appends( &ret, "on line " );
+ smart_str_append_long( &ret, (long ) pe->line );
+ any = 1;
+ }
+ if( pe->offset >= 0 ) {
+ if( any )
+ smart_str_appends( &ret, ", " );
+ else
+ smart_str_appends( &ret, "at " );
+
+ smart_str_appends( &ret, "offset " );
+ smart_str_append_long( &ret, (long ) pe->offset );
+ any = 1;
+ }
+
+ if (pe->preContext[0] != 0 ) {
+ if( any )
+ smart_str_appends( &ret, ", " );
+
+ smart_str_appends( &ret, "after \"" );
+ intl_convert_utf16_to_utf8( &buf, &u8len, pe->preContext, -1, &status );
+ if( U_FAILURE( status ) )
+ {
+ smart_str_appends( &ret, "(could not convert parser error pre-context to UTF-8)" );
+ }
+ else {
+ smart_str_appendl( &ret, buf, u8len );
+ efree( buf );
+ }
+ smart_str_appends( &ret, "\"" );
+ any = 1;
+ }
+
+ if( pe->postContext[0] != 0 )
+ {
+ if( any )
+ smart_str_appends( &ret, ", " );
+
+ smart_str_appends( &ret, "before or at \"" );
+ intl_convert_utf16_to_utf8( &buf, &u8len, pe->postContext, -1, &status );
+ if( U_FAILURE( status ) )
+ {
+ smart_str_appends( &ret, "(could not convert parser error post-context to UTF-8)" );
+ }
+ else
+ {
+ smart_str_appendl( &ret, buf, u8len );
+ efree( buf );
+ }
+ smart_str_appends( &ret, "\"" );
+ any = 1;
+ }
+
+ if( !any )
+ {
+ smart_str_free( &ret );
+ smart_str_appends( &ret, "no parse error" );
+ }
+
+ smart_str_0( &ret );
+ return ret;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/transliterator/transliterator.h b/ext/intl/transliterator/transliterator.h
new file mode 100644
index 0000000..cfd5d38
--- /dev/null
+++ b/ext/intl/transliterator/transliterator.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@netcabo.ot> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef TRANSLITERATOR_TRANSLITERATOR_H
+#define TRANSLITERATOR_TRANSLITERATOR_H
+
+#include <php.h>
+#include <unicode/utypes.h>
+#include <unicode/utrans.h>
+
+#include "ext/standard/php_smart_str.h"
+
+void transliterator_register_constants( INIT_FUNC_ARGS );
+smart_str transliterator_parse_error_to_string( UParseError* pe );
+
+#endif /* #ifndef TRANSLITERATOR_TRANSLITERATOR_H */
diff --git a/ext/intl/transliterator/transliterator_class.c b/ext/intl/transliterator/transliterator_class.c
new file mode 100644
index 0000000..5ef80fb
--- /dev/null
+++ b/ext/intl/transliterator/transliterator_class.c
@@ -0,0 +1,434 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#include "transliterator_class.h"
+#include "php_intl.h"
+#include "transliterator_methods.h"
+#include "intl_error.h"
+#include "intl_convert.h"
+#include "intl_data.h"
+
+#include <unicode/utrans.h>
+
+zend_class_entry *Transliterator_ce_ptr = NULL;
+
+zend_object_handlers Transliterator_handlers;
+
+/* {{{ int transliterator_object_construct( zval *object, UTransliterator *utrans, UErrorCode *status TSRMLS_DC )
+ * Initialize internals of Transliterator_object.
+ */
+int transliterator_object_construct( zval *object,
+ UTransliterator *utrans,
+ UErrorCode *status TSRMLS_DC )
+{
+ const UChar *ustr_id;
+ int32_t ustr_id_len;
+ char *str_id;
+ int str_id_len;
+ Transliterator_object *to;
+
+ TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ assert( to->utrans == NULL );
+ /* this assignment must happen before any return with failure because the
+ * caller relies on it always being made (so it can just destroy the object
+ * to close the transliterator) */
+ to->utrans = utrans;
+
+ ustr_id = utrans_getUnicodeID( utrans, &ustr_id_len );
+ intl_convert_utf16_to_utf8( &str_id, &str_id_len, ustr_id, (int ) ustr_id_len, status );
+ if( U_FAILURE( *status ) )
+ {
+ return FAILURE;
+ }
+
+ zend_update_property_stringl( Transliterator_ce_ptr, object,
+ "id", sizeof( "id" ) - 1, str_id, str_id_len TSRMLS_CC );
+ efree( str_id );
+ return SUCCESS;
+}
+/* }}} */
+
+/*
+ * Auxiliary functions needed by objects of 'Transliterator' class
+ */
+
+/* {{{ void transliterator_object_init( Transliterator_object* to )
+ * Initialize internals of Transliterator_object.
+ */
+static void transliterator_object_init( Transliterator_object* to TSRMLS_DC )
+{
+ if( !to )
+ return;
+
+ intl_error_init( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ void transliterator_object_destroy( Transliterator_object* to )
+ * Clean up mem allocted by internals of Transliterator_object
+ */
+static void transliterator_object_destroy( Transliterator_object* to TSRMLS_DC )
+{
+ if( !to )
+ return;
+
+ if( to->utrans )
+ {
+ utrans_close( to->utrans );
+ to->utrans = NULL;
+ }
+
+ intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ Transliterator_objects_dtor */
+static void Transliterator_objects_dtor(
+ void *object,
+ zend_object_handle handle TSRMLS_DC )
+{
+ zend_objects_destroy_object( object, handle TSRMLS_CC );
+}
+/* }}} */
+
+/* {{{ Transliterator_objects_free */
+static void Transliterator_objects_free( zend_object *object TSRMLS_DC )
+{
+ Transliterator_object* to = (Transliterator_object*) object;
+
+ zend_object_std_dtor( &to->zo TSRMLS_CC );
+
+ transliterator_object_destroy( to TSRMLS_CC );
+
+ efree( to );
+}
+/* }}} */
+
+/* {{{ Transliterator_object_create */
+static zend_object_value Transliterator_object_create(
+ zend_class_entry *ce TSRMLS_DC )
+{
+ zend_object_value retval;
+ Transliterator_object* intern;
+
+ intern = ecalloc( 1, sizeof( Transliterator_object ) );
+
+ zend_object_std_init( &intern->zo, ce TSRMLS_CC );
+#if PHP_VERSION_ID < 50399
+ zend_hash_copy( intern->zo.properties, &(ce->default_properties ),
+ (copy_ctor_func_t) zval_add_ref, NULL, sizeof( zval* ) );
+#else
+ object_properties_init( (zend_object*) intern, ce );
+#endif
+ transliterator_object_init( intern TSRMLS_CC );
+
+ retval.handle = zend_objects_store_put(
+ intern,
+ Transliterator_objects_dtor,
+ (zend_objects_free_object_storage_t) Transliterator_objects_free,
+ NULL TSRMLS_CC );
+
+ retval.handlers = &Transliterator_handlers;
+
+ return retval;
+}
+/* }}} */
+
+/*
+ * Object handlers for Transliterator class (and subclasses)
+ */
+
+/* {{{ clone handler for Transliterator */
+static zend_object_value Transliterator_clone_obj( zval *object TSRMLS_DC )
+{
+ Transliterator_object *to_orig,
+ *to_new;
+ zend_object_value ret_val;
+ intl_error_reset( NULL TSRMLS_CC );
+
+ to_orig = zend_object_store_get_object( object TSRMLS_CC );
+ intl_error_reset( INTL_DATA_ERROR_P( to_orig ) TSRMLS_CC );
+ ret_val = Transliterator_ce_ptr->create_object( Z_OBJCE_P( object ) TSRMLS_CC );
+ to_new = zend_object_store_get_object_by_handle( ret_val.handle TSRMLS_CC );
+
+ zend_objects_clone_members( &to_new->zo, ret_val,
+ &to_orig->zo, Z_OBJ_HANDLE_P( object ) TSRMLS_CC );
+
+ if( to_orig->utrans != NULL )
+ {
+ UTransliterator *utrans = NULL;
+ zval tempz; /* dummy zval to pass to transliterator_object_construct */
+
+ /* guaranteed to return NULL if it fails */
+ utrans = utrans_clone( to_orig->utrans, TRANSLITERATOR_ERROR_CODE_P( to_orig ) );
+
+ if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to_orig ) ) )
+ goto err;
+
+ Z_OBJVAL( tempz ) = ret_val;
+ transliterator_object_construct( &tempz, utrans,
+ TRANSLITERATOR_ERROR_CODE_P( to_orig ) TSRMLS_CC );
+
+ if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to_orig ) ) )
+ {
+ char *err_msg;
+err:
+
+ if( utrans != NULL )
+ transliterator_object_destroy( to_new TSRMLS_CC );
+
+ /* set the error anyway, in case in the future we decide not to
+ * throw an error. It also helps build the error message */
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE( to_orig ) TSRMLS_CC );
+ intl_errors_set_custom_msg( TRANSLITERATOR_ERROR_P( to_orig ),
+ "Could not clone transliterator", 0 TSRMLS_CC );
+
+ err_msg = intl_error_get_message( TRANSLITERATOR_ERROR_P( to_orig ) TSRMLS_CC );
+ php_error_docref( NULL TSRMLS_CC, E_ERROR, "%s", err_msg );
+ efree( err_msg ); /* if it's changed into a warning */
+ /* do not destroy tempz; we need to return something */
+ }
+ }
+ else
+ {
+ /* We shouldn't have unconstructed objects in the first place */
+ php_error_docref( NULL TSRMLS_CC, E_WARNING,
+ "Cloning unconstructed transliterator." );
+ }
+
+ return ret_val;
+}
+/* }}} */
+
+#if PHP_VERSION_ID >= 50399
+# define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG \
+ zval tmp_member; \
+ if( Z_TYPE_P( member ) != IS_STRING ) \
+ { \
+ tmp_member = *member; \
+ zval_copy_ctor( &tmp_member ); \
+ convert_to_string( &tmp_member ); \
+ member = &tmp_member; \
+ key = NULL; \
+ }
+#else
+# define TRANSLITERATOR_PROPERTY_HANDLER_PROLOG \
+ zval tmp_member; \
+ if( Z_TYPE_P( member ) != IS_STRING ) \
+ { \
+ tmp_member = *member; \
+ zval_copy_ctor( &tmp_member ); \
+ convert_to_string( &tmp_member ); \
+ member = &tmp_member; \
+ }
+#endif
+
+#define TRANSLITERATOR_PROPERTY_HANDLER_EPILOG \
+ if( member == &tmp_member ) \
+ { \
+ zval_dtor( &tmp_member ); \
+ }
+
+/* {{{ get_property_ptr_ptr handler */
+#if PHP_VERSION_ID < 50399
+static zval **Transliterator_get_property_ptr_ptr( zval *object, zval *member TSRMLS_DC )
+#else
+static zval **Transliterator_get_property_ptr_ptr( zval *object, zval *member,
+ const struct _zend_literal *key TSRMLS_DC )
+#endif
+{
+ zval **retval;
+
+ TRANSLITERATOR_PROPERTY_HANDLER_PROLOG;
+
+ if(zend_binary_strcmp( "id", sizeof( "id" ) - 1,
+ Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 )
+ {
+ retval = NULL; /* fallback to read_property */
+ }
+ else
+ {
+#if PHP_VERSION_ID < 50399
+ retval = std_object_handlers.get_property_ptr_ptr( object, member TSRMLS_CC );
+#else
+ retval = std_object_handlers.get_property_ptr_ptr( object, member, key TSRMLS_CC );
+#endif
+ }
+
+ TRANSLITERATOR_PROPERTY_HANDLER_EPILOG;
+
+ return retval;
+}
+/* }}} */
+
+/* {{{ read_property handler */
+#if PHP_VERSION_ID < 50399
+static zval *Transliterator_read_property( zval *object, zval *member, int type TSRMLS_DC ) /* {{{ */
+#else
+static zval *Transliterator_read_property( zval *object, zval *member, int type,
+ const struct _zend_literal *key TSRMLS_DC ) /* {{{ */
+#endif
+{
+ zval *retval;
+
+ TRANSLITERATOR_PROPERTY_HANDLER_PROLOG;
+
+ if( ( type != BP_VAR_R && type != BP_VAR_IS ) &&
+ ( zend_binary_strcmp( "id", sizeof( "id" ) - 1,
+ Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) )
+ {
+ php_error_docref0( NULL TSRMLS_CC, E_WARNING, "The property \"id\" is read-only" );
+ retval = &EG( uninitialized_zval );
+ }
+ else
+ {
+#if PHP_VERSION_ID < 50399
+ retval = std_object_handlers.read_property( object, member, type TSRMLS_CC );
+#else
+ retval = std_object_handlers.read_property( object, member, type, key TSRMLS_CC );
+#endif
+ }
+
+ TRANSLITERATOR_PROPERTY_HANDLER_EPILOG;
+
+ return retval;
+}
+
+/* }}} */
+
+/* {{{ write_property handler */
+#if PHP_VERSION_ID < 50399
+static void Transliterator_write_property( zval *object, zval *member, zval *value TSRMLS_DC )
+#else
+static void Transliterator_write_property( zval *object, zval *member, zval *value,
+ const struct _zend_literal *key TSRMLS_DC )
+#endif
+{
+ TRANSLITERATOR_PROPERTY_HANDLER_PROLOG;
+
+ if( ( EG( scope ) != Transliterator_ce_ptr ) &&
+ ( zend_binary_strcmp( "id", sizeof( "id" ) - 1,
+ Z_STRVAL_P( member ), Z_STRLEN_P( member ) ) == 0 ) )
+ {
+ php_error_docref0( NULL TSRMLS_CC, E_WARNING, "The property \"id\" is read-only" );
+ }
+ else
+ {
+#if PHP_VERSION_ID < 50399
+ std_object_handlers.write_property( object, member, value TSRMLS_CC );
+#else
+ std_object_handlers.write_property( object, member, value, key TSRMLS_CC );
+#endif
+ }
+
+ TRANSLITERATOR_PROPERTY_HANDLER_EPILOG;
+}
+/* }}} */
+
+/*
+ * 'Transliterator' class registration structures & functions
+ */
+
+/* {{{ Transliterator methods arguments info */
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_void, 0, 0, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, id )
+ ZEND_ARG_INFO( 0, direction )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_from_rules, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, rules )
+ ZEND_ARG_INFO( 0, direction )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_create_inverse, 0, 0, 1 )
+ ZEND_ARG_OBJ_INFO( 0, orig_trans, Transliterator, 0 )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_me_transliterate, 0, 0, 1 )
+ ZEND_ARG_INFO( 0, subject )
+ ZEND_ARG_INFO( 0, start )
+ ZEND_ARG_INFO( 0, end )
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX( ainfo_trans_error, 0, 0, 1 )
+ ZEND_ARG_OBJ_INFO( 0, trans, Transliterator, 0 )
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+/* {{{ Transliterator_class_functions
+ * Every 'Transliterator' class method has an entry in this table
+ */
+zend_function_entry Transliterator_class_functions[] = {
+ PHP_ME( Transliterator, __construct, ainfo_trans_void, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR | ZEND_ACC_FINAL )
+ PHP_ME_MAPPING( create, transliterator_create, ainfo_trans_create, ZEND_ACC_STATIC |ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( createFromRules,transliterator_create_from_rules, ainfo_trans_create_from_rules, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( createInverse, transliterator_create_inverse, ainfo_trans_void, ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( listIDs, transliterator_list_ids, ainfo_trans_void, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( transliterate, transliterator_transliterate, ainfo_trans_me_transliterate, ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( getErrorCode, transliterator_get_error_code, ainfo_trans_void, ZEND_ACC_PUBLIC )
+ PHP_ME_MAPPING( getErrorMessage,transliterator_get_error_message, ainfo_trans_void, ZEND_ACC_PUBLIC )
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ transliterator_register_Transliterator_class
+ * Initialize 'Transliterator' class
+ */
+void transliterator_register_Transliterator_class( TSRMLS_D )
+{
+ zend_class_entry ce;
+
+ /* Create and register 'Transliterator' class. */
+ INIT_CLASS_ENTRY( ce, "Transliterator", Transliterator_class_functions );
+ ce.create_object = Transliterator_object_create;
+ Transliterator_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC );
+ memcpy( &Transliterator_handlers, zend_get_std_object_handlers(),
+ sizeof Transliterator_handlers );
+ Transliterator_handlers.clone_obj = Transliterator_clone_obj;
+ Transliterator_handlers.get_property_ptr_ptr = Transliterator_get_property_ptr_ptr;
+ Transliterator_handlers.read_property = Transliterator_read_property;
+ Transliterator_handlers.write_property = Transliterator_write_property;
+
+ /* Declare 'Transliterator' class properties */
+ if( !Transliterator_ce_ptr )
+ {
+ zend_error( E_ERROR,
+ "Transliterator: attempt to create properties "
+ "on a non-registered class." );
+ return;
+ }
+ zend_declare_property_null( Transliterator_ce_ptr,
+ "id", sizeof( "id" ) - 1, ZEND_ACC_PUBLIC TSRMLS_CC );
+
+ /* constants are declared in transliterator_register_constants, called from MINIT */
+
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/transliterator/transliterator_class.h b/ext/intl/transliterator/transliterator_class.h
new file mode 100644
index 0000000..5ca50ed
--- /dev/null
+++ b/ext/intl/transliterator/transliterator_class.h
@@ -0,0 +1,65 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@netcabo.pt> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef TRANSLITERATOR_CLASS_H
+#define TRANSLITERATOR_CLASS_H
+
+#include <php.h>
+
+#include "intl_common.h"
+#include "intl_error.h"
+
+#include <unicode/utrans.h>
+
+typedef struct {
+ zend_object zo;
+
+ // error handling
+ intl_error err;
+
+ // ICU transliterator
+ UTransliterator* utrans;
+} Transliterator_object;
+
+#define TRANSLITERATOR_FORWARD UTRANS_FORWARD
+#define TRANSLITERATOR_REVERSE UTRANS_REVERSE
+
+#define TRANSLITERATOR_ERROR( co ) (co)->err
+#define TRANSLITERATOR_ERROR_P( co ) &(TRANSLITERATOR_ERROR( co ))
+
+#define TRANSLITERATOR_ERROR_CODE( co ) INTL_ERROR_CODE(TRANSLITERATOR_ERROR( co ))
+#define TRANSLITERATOR_ERROR_CODE_P( co ) &(INTL_ERROR_CODE(TRANSLITERATOR_ERROR( co )))
+
+#define TRANSLITERATOR_METHOD_INIT_VARS INTL_METHOD_INIT_VARS( Transliterator, to )
+#define TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK INTL_METHOD_FETCH_OBJECT( Transliterator, to )
+#define TRANSLITERATOR_METHOD_FETCH_OBJECT\
+ TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; \
+ if( to->utrans == NULL ) \
+ { \
+ intl_errors_set( &to->err, U_ILLEGAL_ARGUMENT_ERROR, "Found unconstructed transliterator", 0 TSRMLS_CC ); \
+ RETURN_FALSE; \
+ }
+
+int transliterator_object_construct( zval *object,
+ UTransliterator *utrans,
+ UErrorCode *status TSRMLS_DC );
+
+void transliterator_register_Transliterator_class( TSRMLS_D );
+
+extern zend_class_entry *Transliterator_ce_ptr;
+extern zend_object_handlers Transliterator_handlers;
+
+#endif /* #ifndef TRANSLITERATOR_CLASS_H */
diff --git a/ext/intl/transliterator/transliterator_methods.c b/ext/intl/transliterator/transliterator_methods.c
new file mode 100644
index 0000000..d0cfb97
--- /dev/null
+++ b/ext/intl/transliterator/transliterator_methods.c
@@ -0,0 +1,542 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php_intl.h"
+#include "transliterator.h"
+#include "transliterator_class.h"
+#include "transliterator_methods.h"
+#include "intl_data.h"
+#include "intl_convert.h"
+
+#include <zend_exceptions.h>
+
+static int create_transliterator( char *str_id, int str_id_len, long direction, zval *object TSRMLS_DC )
+{
+ Transliterator_object *to;
+ UChar *ustr_id = NULL;
+ int32_t ustr_id_len = 0;
+ UTransliterator *utrans;
+ UParseError parse_error = {0, -1};
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if( ( direction != TRANSLITERATOR_FORWARD ) && (direction != TRANSLITERATOR_REVERSE ) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_create: invalid direction", 0 TSRMLS_CC );
+ return FAILURE;
+ }
+
+ object_init_ex( object, Transliterator_ce_ptr );
+ TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; /* fetch zend object from zval "object" into "to" */
+
+ /* Convert transliterator id to UTF-16 */
+ intl_convert_utf8_to_utf16( &ustr_id, &ustr_id_len, str_id, str_id_len, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
+ {
+ intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC );
+ intl_error_set_custom_msg( NULL, "String conversion of id to UTF-16 failed", 0 TSRMLS_CC );
+ zval_dtor( object );
+ return FAILURE;
+ }
+
+ /* Open ICU Transliterator. */
+ utrans = utrans_openU( ustr_id, ustr_id_len, (UTransDirection ) direction,
+ NULL, -1, &parse_error, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ if (ustr_id) {
+ efree( ustr_id );
+ }
+
+ if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
+ {
+ char *buf = NULL;
+ intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC );
+ spprintf( &buf, 0, "transliterator_create: unable to open ICU transliterator"
+ " with id \"%s\"", str_id );
+ if( buf == NULL ) {
+ intl_error_set_custom_msg( NULL,
+ "transliterator_create: unable to open ICU transliterator", 0 TSRMLS_CC );
+ }
+ else
+ {
+ intl_error_set_custom_msg( NULL, buf, /* copy message */ 1 TSRMLS_CC );
+ efree( buf );
+ }
+ zval_dtor( object );
+ return FAILURE;
+ }
+
+ transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC );
+ /* no need to close the transliterator manually on construction error */
+ if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
+ {
+ intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC );
+ intl_error_set_custom_msg( NULL,
+ "transliterator_create: internal constructor call failed", 0 TSRMLS_CC );
+ zval_dtor( object );
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+/* {{{ proto Transliterator transliterator_create( string id [, int direction ] )
+ * proto Transliterator Transliterator::create( string id [, int direction ] )
+ * Opens a transliterator by id.
+ */
+PHP_FUNCTION( transliterator_create )
+{
+ char *str_id;
+ int str_id_len;
+ long direction = TRANSLITERATOR_FORWARD;
+ int res;
+
+ TRANSLITERATOR_METHOD_INIT_VARS;
+
+ (void) to; /* unused */
+
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
+ &str_id, &str_id_len, &direction ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_create: bad arguments", 0 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ object = return_value;
+ res = create_transliterator( str_id, str_id_len, direction, object TSRMLS_CC );
+ if( res == FAILURE )
+ RETURN_NULL();
+
+ /* success, leave return_value as it is (set by create_transliterator) */
+}
+/* }}} */
+
+/* {{{ proto Transliterator transliterator_create_from_rules( string rules [, int direction ] )
+ * proto Transliterator Transliterator::createFromRules( string rules [, int direction ] )
+ * Opens a transliterator by id.
+ */
+PHP_FUNCTION( transliterator_create_from_rules )
+{
+ char *str_rules;
+ int str_rules_len;
+ UChar *ustr_rules = NULL;
+ int32_t ustr_rules_len = 0;
+ long direction = TRANSLITERATOR_FORWARD;
+ UParseError parse_error = {0, -1};
+ UTransliterator *utrans;
+ UChar id[] = {0x52, 0x75, 0x6C, 0x65, 0x73, 0x54, 0x72,
+ 0x61, 0x6E, 0x73, 0x50, 0x48, 0x50, 0}; /* RulesTransPHP */
+ TRANSLITERATOR_METHOD_INIT_VARS;
+
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|l",
+ &str_rules, &str_rules_len, &direction ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_create_from_rules: bad arguments", 0 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ if( ( direction != TRANSLITERATOR_FORWARD ) && (direction != TRANSLITERATOR_REVERSE ) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_create_from_rules: invalid direction", 0 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ object = return_value;
+ object_init_ex( object, Transliterator_ce_ptr );
+ TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK;
+
+ intl_convert_utf8_to_utf16( &ustr_rules, &ustr_rules_len,
+ str_rules, str_rules_len, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ /* (I'm not a big fan of non-obvious flow control macros ).
+ * This one checks the error value, destroys object and returns false */
+ INTL_CTOR_CHECK_STATUS( to, "String conversion of rules to UTF-16 failed" );
+
+ /* Open ICU Transliterator. */
+ utrans = utrans_openU( id, ( sizeof( id ) - 1 ) / ( sizeof( *id ) ), (UTransDirection ) direction,
+ ustr_rules, ustr_rules_len, &parse_error, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ if (ustr_rules) {
+ efree( ustr_rules );
+ }
+
+ intl_error_set_code( NULL, INTL_DATA_ERROR_CODE( to ) TSRMLS_CC );
+ if( U_FAILURE( INTL_DATA_ERROR_CODE( to ) ) )
+ {
+ char *msg = NULL;
+ smart_str parse_error_str;
+ parse_error_str = transliterator_parse_error_to_string( &parse_error );
+ spprintf( &msg, 0, "transliterator_create_from_rules: unable to "
+ "create ICU transliterator from rules (%s)", parse_error_str.c );
+ smart_str_free( &parse_error_str );
+ if( msg != NULL )
+ {
+ intl_errors_set_custom_msg( INTL_DATA_ERROR_P( to ), msg, 1 TSRMLS_CC );
+ efree( msg );
+ }
+ zval_dtor( return_value );
+ RETURN_NULL();
+ }
+ transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC );
+ /* no need to close the transliterator manually on construction error */
+ INTL_CTOR_CHECK_STATUS( to, "transliterator_create_from_rules: internal constructor call failed" );
+}
+/* }}} */
+
+/* {{{ proto Transliterator transliterator_create_inverse( Transliterator orig_trans )
+ * proto Transliterator Transliterator::createInverse()
+ * Opens the inverse transliterator transliterator.
+ */
+PHP_FUNCTION( transliterator_create_inverse )
+{
+ Transliterator_object *to_orig;
+ UTransliterator *utrans;
+ TRANSLITERATOR_METHOD_INIT_VARS;
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Transliterator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_create_inverse: bad arguments", 0 TSRMLS_CC );
+ RETURN_NULL();
+ }
+
+ TRANSLITERATOR_METHOD_FETCH_OBJECT;
+ to_orig = to;
+
+ object = return_value;
+ object_init_ex( object, Transliterator_ce_ptr );
+ TRANSLITERATOR_METHOD_FETCH_OBJECT_NO_CHECK; /* change "to" into new object (from "object" ) */
+
+ utrans = utrans_openInverse( to_orig->utrans, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ INTL_CTOR_CHECK_STATUS( to, "transliterator_create_inverse: could not create "
+ "inverse ICU transliterator" );
+ transliterator_object_construct( object, utrans, TRANSLITERATOR_ERROR_CODE_P( to ) TSRMLS_CC );
+ /* no need to close the transliterator manually on construction error */
+ INTL_CTOR_CHECK_STATUS( to, "transliterator_create: internal constructor call failed" );
+}
+/* }}} */
+
+/* {{{ proto array transliterator_list_ids()
+ * proto array Transliterator::listIDs()
+ * Return an array with the registered transliterator IDs.
+ */
+PHP_FUNCTION( transliterator_list_ids )
+{
+ UEnumeration *en;
+ const UChar *elem;
+ int32_t elem_len;
+ UErrorCode status = U_ZERO_ERROR;
+
+ intl_error_reset( NULL TSRMLS_CC );
+
+ if( zend_parse_parameters_none() == FAILURE )
+ {
+ /* seems to be the convention in this lib to return false instead of
+ * null on bad parameter types, except on constructors and factory
+ * methods */
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_list_ids: bad arguments", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ en = utrans_openIDs( &status );
+ INTL_CHECK_STATUS( status,
+ "transliterator_list_ids: Failed to obtain registered transliterators" );
+
+ array_init( return_value );
+ while( (elem = uenum_unext( en, &elem_len, &status )) )
+ {
+ char *el_char = NULL;
+ int el_len = 0;
+
+ intl_convert_utf16_to_utf8( &el_char, &el_len, elem, elem_len, &status );
+
+ if( U_FAILURE( status ) )
+ {
+ efree( el_char );
+ break;
+ }
+ else
+ {
+ add_next_index_stringl( return_value, el_char, el_len, 0 );
+ }
+ }
+ uenum_close( en );
+
+ intl_error_set_code( NULL, status TSRMLS_CC );
+ if( U_FAILURE( status ) )
+ {
+ zval_dtor( return_value );
+ RETVAL_FALSE;
+ intl_error_set_custom_msg( NULL, "transliterator_list_ids: "
+ "Failed to build array of registered transliterators", 0 TSRMLS_CC );
+ }
+}
+/* }}} */
+
+/* {{{ proto string transliterator_transliterate( Transliterator trans, string subject [, int start = 0 [, int end = -1 ]] )
+ * proto string Transliterator::transliterate( string subject [, int start = 0 [, int end = -1 ]] )
+ * Transliterate a string. */
+PHP_FUNCTION( transliterator_transliterate )
+{
+ char *str;
+ UChar *ustr = NULL,
+ *uresult = NULL;
+ int str_len;
+ int32_t ustr_len = 0,
+ capacity,
+ uresult_len;
+ long start = 0,
+ limit = -1;
+ int success = 0,
+ temp_trans = 0;
+ TRANSLITERATOR_METHOD_INIT_VARS;
+
+ object = getThis();
+
+ if( object == NULL )
+ {
+ /* in non-OOP version, accept both a transliterator and a string */
+ zval **arg1;
+ if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "Zs|ll",
+ &arg1, &str, &str_len, &start, &limit ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_transliterate: bad arguments", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ if( Z_TYPE_PP( arg1 ) == IS_OBJECT &&
+ instanceof_function( Z_OBJCE_PP( arg1 ), Transliterator_ce_ptr TSRMLS_CC ) )
+ {
+ object = *arg1;
+ }
+ else
+ { /* not a transliterator object as first argument */
+ int res;
+ if(Z_TYPE_PP( arg1 ) != IS_STRING )
+ {
+ SEPARATE_ZVAL( arg1 );
+ convert_to_string( *arg1 );
+ }
+ ALLOC_INIT_ZVAL( object );
+ temp_trans = 1;
+ res = create_transliterator( Z_STRVAL_PP( arg1 ), Z_STRLEN_PP( arg1 ),
+ TRANSLITERATOR_FORWARD, object TSRMLS_CC );
+ if( res == FAILURE )
+ {
+ char *message = intl_error_get_message( NULL TSRMLS_CC );
+ php_error_docref0( NULL TSRMLS_CC, E_WARNING, "Could not create "
+ "transliterator with ID \"%s\" (%s)", Z_STRVAL_PP( arg1 ), message );
+ efree( message );
+ /* don't set U_ILLEGAL_ARGUMENT_ERROR to allow fetching of inner error */
+ goto cleanup;
+ }
+ }
+ }
+ else if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|ll",
+ &str, &str_len, &start, &limit ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_transliterate: bad arguments", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ if( limit < -1 )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_transliterate: \"end\" argument should be "
+ "either non-negative or -1", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ if( start < 0 || ((limit != -1 ) && (start > limit )) )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_transliterate: \"start\" argument should be "
+ "non-negative and not bigger than \"end\" (if defined)", 0 TSRMLS_CC );
+ RETURN_FALSE;
+ }
+
+ /* end argument parsing/validation */
+
+ TRANSLITERATOR_METHOD_FETCH_OBJECT;
+
+ intl_convert_utf8_to_utf16( &ustr, &ustr_len, str, str_len,
+ TRANSLITERATOR_ERROR_CODE_P( to ) );
+ INTL_METHOD_CHECK_STATUS( to, "String conversion of string to UTF-16 failed" );
+
+ /* we've started allocating resources, goto from now on */
+
+ if( ( start > ustr_len ) || (( limit != -1 ) && (limit > ustr_len ) ) )
+ {
+ char *msg;
+ spprintf( &msg, 0,
+ "transliterator_transliterate: Neither \"start\" nor the \"end\" "
+ "arguments can exceed the number of UTF-16 code units "
+ "(in this case, %d)", (int) ustr_len );
+ if(msg != NULL )
+ {
+ intl_errors_set( TRANSLITERATOR_ERROR_P( to ), U_ILLEGAL_ARGUMENT_ERROR,
+ msg, 1 TSRMLS_CC );
+ efree( msg );
+ }
+ RETVAL_FALSE;
+ goto cleanup;
+ }
+
+ uresult = safe_emalloc( ustr_len, sizeof( UChar ), 1 * sizeof( UChar ) );
+ capacity = ustr_len + 1;
+
+ while( 1 )
+ {
+ int32_t temp_limit = ( limit == -1 ? ustr_len : (int32_t) limit );
+ memcpy( uresult, ustr, ustr_len * sizeof( UChar ) );
+ uresult_len = ustr_len;
+
+ utrans_transUChars( to->utrans, uresult, &uresult_len, capacity, (int32_t) start,
+ &temp_limit, TRANSLITERATOR_ERROR_CODE_P( to ) );
+ if( TRANSLITERATOR_ERROR_CODE( to ) == U_BUFFER_OVERFLOW_ERROR )
+ {
+ efree( uresult );
+
+ uresult = safe_emalloc( uresult_len, sizeof( UChar ), 1 * sizeof( UChar ) );
+ capacity = uresult_len + 1;
+
+ intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC );
+ }
+ else if(TRANSLITERATOR_ERROR_CODE( to ) == U_STRING_NOT_TERMINATED_WARNING )
+ {
+ uresult = safe_erealloc( uresult, uresult_len, sizeof( UChar ), 1 * sizeof( UChar ) );
+
+ intl_error_reset( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC );
+ break;
+ }
+ else if( U_FAILURE( TRANSLITERATOR_ERROR_CODE( to ) ) )
+ {
+ intl_error_set_code( NULL, TRANSLITERATOR_ERROR_CODE( to ) TSRMLS_CC );
+ intl_errors_set_custom_msg( TRANSLITERATOR_ERROR_P( to ),
+ "transliterator_transliterate: transliteration failed", 0 TSRMLS_CC );
+ goto cleanup;
+ }
+ else
+ break;
+ }
+
+ uresult[uresult_len] = (UChar) 0;
+
+ success = 1;
+
+cleanup:
+ if( ustr )
+ efree( ustr );
+
+ if( success ) {
+ /* frees uresult even on error */
+ INTL_METHOD_RETVAL_UTF8( to, uresult, uresult_len, 1 );
+ }
+ else
+ {
+ if( uresult )
+ efree( uresult );
+ RETVAL_FALSE;
+ }
+
+ if (temp_trans )
+ zval_ptr_dtor( &object );
+}
+/* }}} */
+
+PHP_METHOD( Transliterator, __construct )
+{
+ /* this constructor shouldn't be called as it's private */
+ zend_throw_exception( NULL,
+ "An object of this type cannot be created with the new operator.",
+ 0 TSRMLS_CC );
+}
+
+/* {{{ proto int transliterator_get_error_code( Transliterator trans )
+ * proto int Transliterator::getErrorCode()
+ * Get the last error code for this transliterator.
+ */
+PHP_FUNCTION( transliterator_get_error_code )
+{
+ TRANSLITERATOR_METHOD_INIT_VARS
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Transliterator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_get_error_code: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+ /* Fetch the object (without resetting its last error code ). */
+ to = zend_object_store_get_object( object TSRMLS_CC );
+ if (to == NULL )
+ RETURN_FALSE;
+
+ RETURN_LONG( (long) TRANSLITERATOR_ERROR_CODE( to ) );
+}
+/* }}} */
+
+
+/* {{{ proto string transliterator_get_error_message( Transliterator trans )
+ * proto string Transliterator::getErrorMessage()
+ * Get the last error message for this transliterator.
+ */
+PHP_FUNCTION( transliterator_get_error_message )
+{
+ const char* message = NULL;
+ TRANSLITERATOR_METHOD_INIT_VARS
+
+ if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O",
+ &object, Transliterator_ce_ptr ) == FAILURE )
+ {
+ intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
+ "transliterator_get_error_message: unable to parse input params", 0 TSRMLS_CC );
+
+ RETURN_FALSE;
+ }
+
+
+ /* Fetch the object (without resetting its last error code ). */
+ to = zend_object_store_get_object( object TSRMLS_CC );
+ if (to == NULL )
+ RETURN_FALSE;
+
+ /* Return last error message. */
+ message = intl_error_get_message( TRANSLITERATOR_ERROR_P( to ) TSRMLS_CC );
+ RETURN_STRING( message, 0 );
+}
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/ext/intl/transliterator/transliterator_methods.h b/ext/intl/transliterator/transliterator_methods.h
new file mode 100644
index 0000000..b806de8
--- /dev/null
+++ b/ext/intl/transliterator/transliterator_methods.h
@@ -0,0 +1,38 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Gustavo Lopes <cataphract@php.net> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef TRANSLITERATOR_METHODS_H
+#define TRANSLITERATOR_METHODS_H
+
+#include <php.h>
+
+PHP_FUNCTION( transliterator_create );
+
+PHP_FUNCTION( transliterator_create_from_rules );
+
+PHP_FUNCTION( transliterator_list_ids );
+
+PHP_FUNCTION( transliterator_create_inverse );
+
+PHP_FUNCTION( transliterator_transliterate );
+
+PHP_METHOD( Transliterator, __construct );
+
+PHP_FUNCTION( transliterator_get_error_code );
+
+PHP_FUNCTION( transliterator_get_error_message );
+
+#endif /* #ifndef TRANSLITERATOR_METHODS_H */