php-i18n Digest 21 Dec 2006 18:30:28 -0000 Issue 343

Topics (messages 1038 through 1043):

Re: [PATCH] ICU ResourceBundles for ext/unicode
        1038 by: l0t3k
        1039 by: Michael Wallner
        1040 by: l0t3k
        1041 by: l0t3k
        1042 by: Michael Wallner

Re: ICU ResourceBundles for ext/unicode
        1043 by: Norbert Lindenberg

Administrivia:

To subscribe to the digest, e-mail:
        [EMAIL PROTECTED]

To unsubscribe from the digest, e-mail:
        [EMAIL PROTECTED]

To post to the list, e-mail:
        [email protected]


----------------------------------------------------------------------
--- Begin Message ---
"Michael Wallner" <[EMAIL PROTECTED]> wrote in message 
news:[EMAIL PROTECTED]
> l0t3k wrote:
>> In ICU, resource bundles are cached for efficiency, but the cache is
>> per-process, and i seem to recall that the cache is not currently 
>> flushed.
>> Additionally the key is the ICU locale id, so its impossible without 
>> source
>> changes to make this work for user-created resources without collision in
>> shared hosting mode.
>
> Sounds like gettext() :) Could you elaborate?
its actually worse than gettext()..
suppose we have an Apache process handling PHP scripts in different web 
apps. When a script from app1 requests the resource bundle for "en_US" for 
example, ICU performs the search, and if found the internal resource bundle 
structure is cached. There is a lot that happens behind the scenes, for 
example : file system path search for "en_US", fall back to "en" if not 
found, searching for locales in .dat files and falling back to .res file if 
not found, etc. Caching here makes sense, but the problem is that the cache 
is process wide, and the cache key used is simply the ISO locale id passed 
to the constructor, even if a full path to the resource file was specified. 
There is nothing to distinguish two resource bundles using the same name in 
different web apps, so when app 2 requests a bundle for "en_US", the cache 
returns the bundle from app 1. The cache is also not flushed until the 
process dies.

 Internally, the cache uses a ref count for resource bundles, and there is 
code to do a flush (its private), so technically there is a way to handle 
this, but not without hacking the ICU source. i did this about 2 years ago, 
but the machine i used was stolen ;-(


clayton 

--- End Message ---
--- Begin Message ---
l0t3k wrote:

> its actually worse than gettext()..
> suppose we have an Apache process handling PHP scripts in different web 
> apps. When a script from app1 requests the resource bundle for "en_US" for 
> example, ICU performs the search, and if found the internal resource bundle 
> structure is cached. There is a lot that happens behind the scenes, for 
> example : file system path search for "en_US", fall back to "en" if not 
> found, searching for locales in .dat files and falling back to .res file if 
> not found, etc. Caching here makes sense, but the problem is that the cache 
> is process wide, and the cache key used is simply the ISO locale id passed 
> to the constructor, even if a full path to the resource file was specified. 
> There is nothing to distinguish two resource bundles using the same name in 
> different web apps, so when app 2 requests a bundle for "en_US", the cache 
> returns the bundle from app 1. The cache is also not flushed until the 
> process dies.

Maybe this has changed in the meantime?

[EMAIL PROTECTED]:~/build/php-unicode-debug$ cli -r 'var_dump(new 
ResourceBundle("/home/mike/cvs/php-src/ext/unicode/tests/test"), new 
ResourceBundle("/home/mike/cvs/php-src/ext/unicode/tests/test2"));'
object(ResourceBundle)#1 (4) {
  ["data"]=>
  unicode(5) "value"
  ["greet"]=>
  array(7) {
    ["1%4"]=>
    unicode(13) "Good morning."
    ["1%5"]=>
    unicode(15) "Good afternoon."
    ["1%7"]=>
    unicode(13) "Good evening."
    ["1%8"]=>
    unicode(11) "Good night."
    ["4%14"]=>
    unicode(7) "Please "
    ["4%19"]=>
    unicode(10) "Thank you."
    ["4%20"]=>
    unicode(10) "Sincerely,"
  }
  ["list"]=>
  array(3) {
    ["one"]=>
    int(1)
    ["three"]=>
    string(1) "4"
    ["two"]=>
    unicode(1) "2"
  }
  ["serial"]=>
  array(3) {
    [0]=>
    int(1)
    [1]=>
    int(3)
    [2]=>
    int(5)
  }
}
object(ResourceBundle)#2 (1) {
  ["test"]=>
  unicode(4) "data"
}

It would really sound odd if it would be keyed by the locale only...

>  Internally, the cache uses a ref count for resource bundles, and there is 
> code to do a flush (its private), so technically there is a way to handle 
> this, but not without hacking the ICU source. i did this about 2 years ago, 
> but the machine i used was stolen ;-(
> 
> 
> clayton 


-- 
Michael

--- End Message ---
--- Begin Message ---
Michael,
>
> Maybe this has changed in the meantime?

  its been a while since i looked at this. i'll take a look and see..

btw. i assume you'll handle open basedir issues as well ?

clayton

--- End Message ---
--- Begin Message ---
Mike,
  fyi, your messages are being cc'ed to the icu list

clayton

"Michael Wallner" <[EMAIL PROTECTED]> wrote in message 
news:[EMAIL PROTECTED]
> l0t3k wrote:
>

--- End Message ---
--- Begin Message ---
l0t3k wrote:
>   its been a while since i looked at this. i'll take a look and see..

Well, I see, it may be true for opening the ICUDATA bundle... but that 
shouldn't be a problem.

> btw. i assume you'll handle open basedir issues as well ?

There's no such thing in PHP6.


Latest patch attached.
I added an u_catgets() interface and a tree path accessor.

Regards,
-- 
Michael
? ext/unicode/tests/test2_root.res
? ext/unicode/tests/test2_root.txt
Index: ext/unicode/config.m4
===================================================================
RCS file: /repository/php-src/ext/unicode/config.m4,v
retrieving revision 1.10
diff -u -p -d -r1.10 config.m4
--- ext/unicode/config.m4       13 Jun 2006 23:46:04 -0000      1.10
+++ ext/unicode/config.m4       14 Dec 2006 23:10:29 -0000
@@ -4,4 +4,4 @@ dnl
 
 PHP_SUBST(UNICODE_SHARED_LIBADD)
 AC_DEFINE(HAVE_UNICODE, 1, [ ])
-PHP_NEW_EXTENSION(unicode, unicode.c locale.c unicode_iterators.c collator.c 
property.c constants.c transform.c, $ext_shared)
+PHP_NEW_EXTENSION(unicode, unicode.c locale.c unicode_iterators.c collator.c 
property.c constants.c transform.c resourcebundle.c, $ext_shared)
Index: ext/unicode/constants.c
===================================================================
RCS file: /repository/php-src/ext/unicode/constants.c,v
retrieving revision 1.1
diff -u -p -d -r1.1 constants.c
--- ext/unicode/constants.c     5 May 2006 20:56:21 -0000       1.1
+++ ext/unicode/constants.c     14 Dec 2006 23:10:29 -0000
@@ -16,6 +16,7 @@
 */
 
 #include "php_unicode.h"
+#include <unicode/ures.h>
 
 PHPAPI zend_class_entry *u_const_ce;
 
@@ -578,6 +579,28 @@ static void php_register_misc_constants(
 }
 /* }}} */
 
+/* {{{ ResourceBundle constants */
+static void php_register_resourcebundle_constants(TSRMLS_D)
+{
+       REGISTER_U_CONST(URES_NONE);
+       REGISTER_U_CONST(URES_STRING);
+       REGISTER_U_CONST(URES_BINARY);
+       REGISTER_U_CONST(URES_TABLE);
+       REGISTER_U_CONST(URES_ALIAS);
+       REGISTER_U_CONST(URES_INT);
+       REGISTER_U_CONST(URES_ARRAY);
+       REGISTER_U_CONST(URES_INT_VECTOR);
+}
+/* }}} */
+
+/* {{{ LocaleData constants */
+static void php_register_localedata_constants(TSRMLS_D)
+{
+       REGISTER_U_CONST(ULOC_ACTUAL_LOCALE);
+       REGISTER_U_CONST(ULOC_VALID_LOCALE);
+}
+/* }}} */
+
 void php_register_unicode_constants(TSRMLS_D)
 {
        zend_class_entry ce;
@@ -601,6 +624,8 @@ void php_register_unicode_constants(TSRM
        php_register_numeric_type_constants(TSRMLS_C);
        php_register_hangul_syllable_constants(TSRMLS_C);
        php_register_misc_constants(TSRMLS_C);
+       php_register_resourcebundle_constants(TSRMLS_C);
+       php_register_localedata_constants(TSRMLS_C);
 }
 
 /*
Index: ext/unicode/php_unicode.h
===================================================================
RCS file: /repository/php-src/ext/unicode/php_unicode.h,v
retrieving revision 1.14
diff -u -p -d -r1.14 php_unicode.h
--- ext/unicode/php_unicode.h   5 May 2006 20:56:21 -0000       1.14
+++ ext/unicode/php_unicode.h   14 Dec 2006 23:10:29 -0000
@@ -72,6 +72,7 @@ PHP_FUNCTION(collator_get_default);
 PHP_FUNCTION(collator_set_default);
 PHP_METHOD(collator, __construct);
 
+void php_init_resourcebundle(TSRMLS_D);
 void php_init_collation(TSRMLS_D);
 void php_register_unicode_constants(TSRMLS_D);
 
Index: ext/unicode/resourcebundle.c
===================================================================
RCS file: ext/unicode/resourcebundle.c
diff -N ext/unicode/resourcebundle.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ext/unicode/resourcebundle.c        14 Dec 2006 23:10:29 -0000
@@ -0,0 +1,969 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 6                                                        |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | [EMAIL PROTECTED] so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Authors: Michael Wallner <[EMAIL PROTECTED]>                              
|
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id: collator.c,v 1.11 2006/10/06 17:14:14 andrei Exp $ */
+
+#include "php.h"
+#include <unicode/ures.h>
+#include <unicode/ucat.h>
+
+typedef struct _php_resourcebundle {
+       zend_object zo;
+       UResourceBundle *ures;
+       HashTable *properties_ht_ptr;
+       zval *dimension_zv_ptr;
+} php_resourcebundle;
+
+zend_class_entry *unicode_resourcebundle_ce;
+static zend_object_handlers unicode_resourcebundle_oh;
+
+static PHP_METHOD(ResourceBundle, __construct);
+static PHP_METHOD(ResourceBundle, factory);
+static PHP_METHOD(ResourceBundle, open);
+static PHP_METHOD(ResourceBundle, getVersion);
+static PHP_METHOD(ResourceBundle, getType);
+static PHP_METHOD(ResourceBundle, getLocale);
+static PHP_METHOD(ResourceBundle, getKey);
+static PHP_METHOD(ResourceBundle, getString);
+static PHP_METHOD(ResourceBundle, getStringByIndex);
+static PHP_METHOD(ResourceBundle, getStringByKey);
+static PHP_METHOD(ResourceBundle, getBinary);
+static PHP_METHOD(ResourceBundle, getInt);
+static PHP_METHOD(ResourceBundle, getArray);
+static PHP_METHOD(ResourceBundle, getByIndex);
+static PHP_METHOD(ResourceBundle, getByKey);
+static PHP_METHOD(ResourceBundle, getByPath);
+static PHP_METHOD(ResourceBundle, get);
+static PHP_METHOD(ResourceBundle, count);
+static PHP_METHOD(ResourceBundle, getCat);
+
+static zend_function_entry unicode_resourcebundle_fe[] = {
+       PHP_ME(ResourceBundle, factory, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+       PHP_ME(ResourceBundle, __construct, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, open, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getVersion, NULL, ZEND_ACC_PUBLIC)
+       PHP_MALIAS(ResourceBundle, getSize, count, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getType, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getLocale, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getKey, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getString, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getStringByIndex, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getStringByKey, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getBinary, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getInt, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getArray, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getByIndex, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getByKey, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getByPath, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, get, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, count, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(ResourceBundle, getCat, NULL, ZEND_ACC_PUBLIC)
+       {NULL},
+};
+
+#define IF_RB_INIT(rb) if (!(rb)->ures) { php_error_docref(NULL TSRMLS_CC, 
E_WARNING, "Use of uninitialized ResourceBundle"); } else
+#define IF_RB_TYPE(rb, t) if (ures_getType((rb)->ures) != (t)) { 
php_error_docref(NULL TSRMLS_CC, E_WARNING, "ResourceBundle is not of type 
"#t); } else
+#define IF_RB_SUCCESS(s) if (U_FAILURE(s)) { php_error_docref(NULL TSRMLS_CC, 
E_WARNING, "%s", u_errorName(s)); } else
+
+/* {{{ extractors */
+#define _EXTRACT_ALL 0
+static inline void _extract(UResourceBundle *next, zval *zv, uint types 
TSRMLS_DC);
+
+static inline void _extract_intvector(UResourceBundle *ures, zval *zv 
TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       int32_t len, i;
+       const int32_t *vec = ures_getIntVector(ures, &len, &status);
+       
+       IF_RB_SUCCESS(status) {
+               array_init(zv);
+               for (i = 0; i < len; ++i) {
+                       add_next_index_long(zv, vec[i]);
+               }
+       }
+}
+
+static inline void _extract_array(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       
+       array_init(zv);
+       ures_resetIterator(ures);
+       
+       while (ures_hasNext(ures)) {
+               UResourceBundle *next = ures_getNextResource(ures, NULL, 
&status);
+               
+               IF_RB_SUCCESS(status) {
+                       zval *entry;
+                       
+                       MAKE_STD_ZVAL(entry);
+                       ZVAL_NULL(entry);
+                       _extract(next, entry, _EXTRACT_ALL TSRMLS_CC);
+                       add_next_index_zval(zv, entry);
+               }
+       }
+}
+
+static inline void _extract_table(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       
+       array_init(zv);
+       ures_resetIterator(ures);
+       
+       while (ures_hasNext(ures)) {
+               UResourceBundle *next = ures_getNextResource(ures, NULL, 
&status);
+               
+               IF_RB_SUCCESS(status) {
+                       zval *entry;
+                       
+                       MAKE_STD_ZVAL(entry);
+                       ZVAL_NULL(entry);
+                       _extract(next, entry, _EXTRACT_ALL TSRMLS_CC);
+                       add_assoc_zval(zv, ures_getKey(next), entry);
+               }
+       }
+}
+
+static inline void _extract_string(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       int32_t str_len;
+       const UChar *str_buf = ures_getString(ures, &str_len, &status);
+       
+       IF_RB_SUCCESS(status) {
+               ZVAL_UNICODEL(zv, str_buf, str_len, ZSTR_DUPLICATE);
+       }
+}
+
+static inline void _extract_binary(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       int32_t bin_len;
+       const uint8_t *bin_buf = ures_getBinary(ures, &bin_len, &status);
+       
+       IF_RB_SUCCESS(status) {
+               ZVAL_STRINGL(zv, bin_buf, bin_len, ZSTR_DUPLICATE);
+       }
+}
+
+static inline void _extract_int(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       int32_t intval = ures_getInt(ures, &status);
+       
+       IF_RB_SUCCESS(status) {
+               ZVAL_LONG(zv, intval);
+       }
+}
+
+static inline void _extract_alias(UResourceBundle *ures, zval *zv TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UResourceBundle *alias = ures_getByIndex(ures, 0, NULL, &status);
+       
+       IF_RB_SUCCESS(status) {
+               _extract(alias, zv, _EXTRACT_ALL TSRMLS_CC);
+       }
+}
+
+static inline void _extract(UResourceBundle *ures, zval *zv, uint types 
TSRMLS_DC)
+{
+       UResType type = ures_getType(ures);
+       
+       if (types && !(types & type)) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "ResourceBundle is 
of type %d", type);
+       } else {
+               switch (type) {
+                       case URES_NONE:
+                               break;
+                       case URES_STRING:
+                               _extract_string(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_BINARY:
+                               _extract_binary(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_INT:
+                               _extract_int(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_INT_VECTOR:
+                               _extract_intvector(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_ARRAY:
+                               _extract_array(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_TABLE:
+                               _extract_table(ures, zv TSRMLS_CC);
+                               break;
+                       case URES_ALIAS:
+                               _extract_alias(ures, zv TSRMLS_CC);
+                               break;
+                       default:
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, 
"Unknown ResourceBundle type %d", type);
+                               break;
+               }
+       }
+}
+/* }}} */
+
+/* {{{ zend object ctor/dtor */
+static void unicode_resourcebundle_free_object(zend_object *zo TSRMLS_DC)
+{
+       php_resourcebundle *rb = (php_resourcebundle *) zo;
+       
+       zend_object_std_dtor(zo TSRMLS_CC);
+       if (rb->ures) {
+               ures_close(rb->ures);
+       }
+       if (rb->properties_ht_ptr) {
+               zend_hash_destroy(rb->properties_ht_ptr);
+               FREE_HASHTABLE(rb->properties_ht_ptr);
+       }
+       if(rb->dimension_zv_ptr) {
+               zval_ptr_dtor(&rb->dimension_zv_ptr);
+       }
+       efree(rb);
+}
+
+static zend_object_value 
unicode_resourcebundle_create_object_ex(zend_class_entry *ce, 
php_resourcebundle **ptr, UResourceBundle *ures TSRMLS_DC)
+{
+       zend_object_value ov;
+       php_resourcebundle *rb = ecalloc(1, sizeof(*rb));
+       
+       if (ptr) {
+               *ptr = rb;
+       }
+       if (ures) {
+               rb->ures = ures;
+       }
+       
+       zend_object_std_init((zend_object *) rb, ce TSRMLS_CC);
+       zend_hash_copy(rb->zo.properties, &ce->default_properties, 
(copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       
+       ov.handle = zend_objects_store_put(rb, (zend_objects_store_dtor_t) 
zend_objects_destroy_object, (zend_objects_free_object_storage_t) 
unicode_resourcebundle_free_object, NULL TSRMLS_CC);
+       ov.handlers = &unicode_resourcebundle_oh;
+       
+       return ov;
+}
+
+static zend_object_value unicode_resourcebundle_create_object(zend_class_entry 
*ce TSRMLS_DC)
+{
+       return unicode_resourcebundle_create_object_ex(ce, NULL, NULL 
TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ object handlers */
+static int unicode_resourcebundle_cast_object(zval *this_ptr, zval 
*return_value, int type, void *extra TSRMLS_DC)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       IF_RB_INIT(rb) {
+               /* scalar types are fine for converting */
+               switch (ures_getType(rb->ures)) {
+                       case URES_INT:
+                               _extract_int(rb->ures, return_value TSRMLS_CC);
+                               break;
+                       case URES_STRING:
+                               _extract_string(rb->ures, return_value 
TSRMLS_CC);
+                               break;
+                       case URES_BINARY:
+                               _extract_binary(rb->ures, return_value 
TSRMLS_CC);
+                               break;
+                       default:
+                               return FAILURE;
+               }
+               convert_to_explicit_type(return_value, type);
+               return SUCCESS;
+       }
+       
+       return FAILURE;
+}
+
+/* handles (array) cast */
+static HashTable *unicode_resourcebundle_get_properties(zval *this_ptr 
TSRMLS_DC)
+{
+       zval rv;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       INIT_PZVAL(&rv);
+       ZVAL_NULL(&rv);
+       
+       IF_RB_INIT(rb) {
+               _extract(rb->ures, &rv, URES_ARRAY|URES_TABLE|URES_INT_VECTOR 
TSRMLS_CC);
+       }
+       
+       if (rb->properties_ht_ptr) {
+               zend_hash_destroy(rb->properties_ht_ptr);
+               FREE_HASHTABLE(rb->properties_ht_ptr);
+       }
+       
+       switch (Z_TYPE(rv)) {
+               case IS_ARRAY:
+                       break;
+               case IS_NULL:
+                       array_init(&rv);
+                       break;
+               default:
+                       convert_to_array(&rv);
+                       break;
+       }
+       
+       return rb->properties_ht_ptr = Z_ARRVAL(rv);
+}
+
+static zval *unicode_resourcebundle_read_dimension(zval *this_ptr, zval 
*offset, int type TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UResourceBundle *ures = NULL;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       zval tmp, *return_value = &EG(uninitialized_zval);
+       
+       /* FIXXME: what if type != BP_VAR_R ? */
+       IF_RB_INIT(rb) {
+               switch (Z_TYPE_P(offset)) {
+                       case IS_LONG:
+                               ures = ures_getByIndex(rb->ures, 
Z_LVAL_P(offset), NULL, &status);
+                               break;
+                       case IS_STRING:
+                               ures = ures_getByKey(rb->ures, 
Z_STRVAL_P(offset), NULL, &status);
+                               break;
+                       case IS_UNICODE:
+                               tmp = *offset;
+                               zval_copy_ctor(&tmp);
+                               convert_to_string(&tmp);
+                               ures = ures_getByKey(rb->ures, Z_STRVAL(tmp), 
NULL, &status);
+                               zval_dtor(&tmp);
+                               break;
+               }
+               IF_RB_SUCCESS(status) {
+                       if (ures) {
+                               MAKE_STD_ZVAL(return_value);
+                               Z_TYPE_P(return_value) = IS_OBJECT;
+                               Z_OBJVAL_P(return_value) = 
unicode_resourcebundle_create_object_ex(Z_OBJCE_P(getThis()), NULL, ures 
TSRMLS_CC);
+                               if (rb->dimension_zv_ptr) {
+                                       zval_ptr_dtor(&rb->dimension_zv_ptr);
+                               }
+                               rb->dimension_zv_ptr = return_value;
+                       }
+               }
+       }
+       
+       return return_value;
+}
+
+static int unicode_resourcebundle_has_dimension(zval *this_ptr, zval *member, 
int check_empty TSRMLS_DC)
+{
+       return Z_TYPE_P(unicode_resourcebundle_read_dimension(getThis(), 
member, BP_VAR_R TSRMLS_CC)) == IS_OBJECT;
+}
+
+/* }}} */
+
+/* {{{  iterators */
+typedef struct _php_resourcebundle_iter {
+       zend_object_iterator it;
+       zval *zv;
+       php_resourcebundle *rb;
+       struct {
+               int ref;
+               zval *obj;
+               struct {
+                       char *str;
+                       int len;
+                       ulong num;
+               } key;
+       } next;
+} php_resourcebundle_iter;
+
+static inline void unicode_resourcebundle_if_dtor_ex(php_resourcebundle_iter 
*data TSRMLS_DC)
+{
+       if (data->next.obj) {
+               zval_ptr_dtor(&data->next.obj);
+               data->next.obj = NULL;
+       }
+       if (data->next.key.str) {
+               efree(data->next.key.str);
+               data->next.key.str = NULL;
+       }
+       data->next.key.num = 0;
+}
+
+static void unicode_resourcebundle_if_dtor(zend_object_iterator *iter 
TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = iter->data;
+       
+       unicode_resourcebundle_if_dtor_ex(data TSRMLS_CC);
+       zval_ptr_dtor(&data->zv);
+       efree(data);
+}
+
+static int unicode_resourcebundle_if_valid(zend_object_iterator *iter 
TSRMLS_DC)
+{
+       return ((php_resourcebundle_iter *) iter->data)->next.obj ? SUCCESS : 
FAILURE;
+}
+
+static void unicode_resourecbundle_if_get_current_data(zend_object_iterator 
*iter, zval ***zv TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = iter->data;
+       
+       /* FIXXME: what if by_ref==1 ?*/
+       if (data->next.obj) {
+               *zv = &data->next.obj;
+       } else {
+               *zv = NULL;
+       }
+}
+
+static int unicode_resourcebundle_if_get_current_key(zend_object_iterator 
*iter, zstr *str_key, uint *str_len, ulong *int_key TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = iter->data;
+       
+       if (data->next.key.str) {
+               /* dup! */
+               str_key->s = estrndup(data->next.key.str, data->next.key.len);
+               *str_len = data->next.key.len + 1;
+               return HASH_KEY_IS_STRING;
+       } else {
+               *int_key = data->next.key.num - 1;
+               return HASH_KEY_IS_LONG;
+       }
+}
+
+static inline void 
unicode_resourcebundle_if_move_forward_ex(php_resourcebundle_iter *data 
TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       const char *key_str;
+       UResourceBundle *ures;
+       
+       unicode_resourcebundle_if_dtor_ex(data TSRMLS_CC);
+       
+       ures = ures_getNextResource(data->rb->ures, NULL, &status);
+       if (U_SUCCESS(status)) {
+               ++data->next.key.num;
+               
+               MAKE_STD_ZVAL(data->next.obj);
+               Z_TYPE_P(data->next.obj) = IS_OBJECT;
+               Z_OBJVAL_P(data->next.obj) = 
unicode_resourcebundle_create_object_ex(data->rb->zo.ce, NULL, ures TSRMLS_CC);
+               
+               if ((key_str = ures_getKey(ures))) {
+                       data->next.key.str = estrndup(key_str, 
data->next.key.len = strlen(key_str));
+               } else {
+                       data->next.key.str = NULL;
+               }
+       }
+}
+
+static void unicode_resourcebundle_if_move_forward(zend_object_iterator *iter 
TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = iter->data;
+       
+       unicode_resourcebundle_if_move_forward_ex(data TSRMLS_CC);
+}
+
+static void unicode_resourcebundle_if_rewind(zend_object_iterator *iter 
TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = iter->data;
+       
+       ures_resetIterator(data->rb->ures);
+       
+       unicode_resourcebundle_if_move_forward_ex(data TSRMLS_CC);
+}
+
+static zend_object_iterator_funcs unicode_resourcebundle_if = {
+       unicode_resourcebundle_if_dtor, /* release all resources associated 
with this iterator instance */
+       unicode_resourcebundle_if_valid, /* check for end of iteration (FAILURE 
or SUCCESS if data is valid) */
+       unicode_resourecbundle_if_get_current_data, /* fetch the item data for 
the current element */
+       unicode_resourcebundle_if_get_current_key, /* fetch the key for the 
current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may 
be NULL) */
+       unicode_resourcebundle_if_move_forward, /* step forwards to next 
element */
+       unicode_resourcebundle_if_rewind, /* rewind to start of data (optional, 
may be NULL) */
+       NULL, /* invalidate current value/key (optional, may be NULL) */
+};
+
+static zend_object_iterator 
*unicode_resourcebundle_get_iterator(zend_class_entry *ce, zval *object, int 
by_ref TSRMLS_DC)
+{
+       php_resourcebundle_iter *data = ecalloc(1, sizeof(*data));
+       
+       ZVAL_ADDREF(object);
+       data->zv = object;
+       data->rb = zend_object_store_get_object(object TSRMLS_CC);
+       data->next.ref = by_ref;
+       
+       data->it.data = data;
+       data->it.funcs = &unicode_resourcebundle_if;
+       
+       return (zend_object_iterator *) data;
+}
+/* }}} */
+
+/* {{{ unicode_resourcebundle_walktree */
+static int unicode_resourcebundle_walktree(UResourceBundle *top_ures, 
UResourceBundle **get_ures, const char *path_str, int path_len, char path_sep 
TSRMLS_DC)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UResourceBundle *tmp_ures;
+       const char *path_ptr, *path_end;
+       long path_num;
+       
+       tmp_ures = top_ures;
+       path_end = path_str + path_len;
+       path_ptr = strchr(path_str, path_sep);
+       
+       while (U_SUCCESS(status)) {
+               UResourceBundle *sub_ures = NULL;
+               
+               if (!path_ptr) {
+                       path_ptr = path_end;
+               }
+               
+               if (IS_LONG == is_numeric_string(path_str, path_ptr - path_str, 
&path_num, NULL, 0)) {
+                       sub_ures = ures_getByIndex(tmp_ures, path_num, NULL, 
&status);
+               } else {
+                       char *path_tmp = estrndup(path_str, path_ptr - 
path_str);
+                       sub_ures = ures_getByKey(tmp_ures, path_tmp, NULL, 
&status);
+                       efree(path_tmp);
+               }
+               
+               if (tmp_ures != top_ures) {
+                       ures_close(tmp_ures);
+               }
+               
+               IF_RB_SUCCESS(status) {
+                       tmp_ures = sub_ures;
+               }
+               
+               if (path_ptr == path_end || path_ptr == path_end - 1) {
+                       break;
+               } else {
+                       path_ptr = strchr(path_str = path_ptr + 1, path_sep);
+               }
+       }
+       
+       if (U_SUCCESS(status)) {
+               *get_ures = tmp_ures;
+               return SUCCESS;
+       }
+       
+       return FAILURE;
+}
+/* }}} */
+
+/* {{{ unicode_resourcebundle_factory */
+static int unicode_resourcebundle_factory(INTERNAL_FUNCTION_PARAMETERS)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       char *package_str = NULL, *locale_str = NULL, *path_str = NULL, 
*sep_str = NULL;
+       int package_len = 0, locale_len = 0, path_len = 0, sep_len = 0;
+       UResourceBundle *top_ures, *sub_ures;
+       
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 
"|s!s!s!s!", &package_str, &package_len, &locale_str, &locale_len, &path_str, 
&path_len, &sep_str, &sep_len)) {
+               php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+               
+               top_ures = ures_open(package_str, locale_str, &status);
+               
+               if (path_len) {
+                       if (SUCCESS == 
unicode_resourcebundle_walktree(top_ures, &sub_ures, path_str, path_len, 
(sep_len ? *sep_str : '/') TSRMLS_CC)) {
+                               ures_close(top_ures);
+                               top_ures = sub_ures;
+                       } else {
+                               ures_close(top_ures);
+                               return FAILURE;
+                       }
+               }
+               
+               if (U_SUCCESS(status)) {
+                       if (rb->ures) {
+                               ures_close(rb->ures);
+                       }
+                       rb->ures = top_ures;
+                       zend_update_property_long(Z_OBJCE_P(getThis()), 
getThis(), ZEND_STRL("warning"), status TSRMLS_CC);
+                       return SUCCESS;
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to 
open resource bundle '%s' for '%s' locale", package_str && *package_str ? 
package_str : "root", locale_str ? *locale_str ? locale_str : "default" : 
"root");
+               }
+       }
+       return FAILURE;
+}
+/* }}} */
+
+/* {{{ proto void ResourceBundle::__construct([string package[, string 
locale[, string tree_path[, string path_sep = "/"]]]])
+       Create a new ResourceBundle object */
+static PHP_METHOD(ResourceBundle, __construct)
+{
+       php_set_error_handling(EH_THROW, NULL TSRMLS_CC);
+       unicode_resourcebundle_factory(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto static ResourceBundle ResourceBundle::factory([string package[, 
string locale[, string tree_path[, string path_sep = "/"]]]])
+       Create a new ResourceBundle object */
+static PHP_METHOD(ResourceBundle, factory)
+{
+       php_set_error_handling(EH_THROW, NULL TSRMLS_CC);
+       getThis() = return_value;
+       object_init_ex(getThis(), unicode_resourcebundle_ce);
+       if (SUCCESS != 
unicode_resourcebundle_factory(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
+               zval_ptr_dtor(&getThis());
+               RETVAL_NULL();
+       }
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto bool ResourceBundle::open([string package[, string locale[, 
string tree_path[, string path_sep = "/"]]]])
+       Open another ResourceBundle */
+static PHP_METHOD(ResourceBundle, open)
+{
+       RETURN_BOOL(SUCCESS == 
unicode_resourcebundle_factory(INTERNAL_FUNCTION_PARAM_PASSTHRU));
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getVersion(void)
+       Get ResourceBundle version */
+static PHP_METHOD(ResourceBundle, getVersion)
+{
+       UVersionInfo version_ptr = {0};
+       char version_str[U_MAX_VERSION_STRING_LENGTH] = {0};
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               ures_getVersion(rb->ures, version_ptr);
+               u_versionToString(version_ptr, version_str);
+               RETURN_ASCII_STRING(version_str, ZSTR_DUPLICATE);
+       }
+}
+/* }}} */
+
+/* {{{ proto int ResourceBundle::getType(void)
+       Get the type of the RespourceBundle */
+static PHP_METHOD(ResourceBundle, getType)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               RETVAL_LONG(ures_getType(rb->ures));
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getLocale([int locale_type = 
U::LOC_VALID_LOCALE])
+       Get ResourceBundle locale */
+static PHP_METHOD(ResourceBundle, getLocale)
+{
+       UErrorCode dummy;
+       long type = ULOC_VALID_LOCALE;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &type);
+       
+       IF_RB_INIT(rb) {
+               RETURN_ASCII_STRING(ures_getLocaleByType(rb->ures, type, 
&dummy), ZSTR_DUPLICATE);
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getKey()
+       Get any key associated eith this ResourceBundle */
+static PHP_METHOD(ResourceBundle, getKey)
+{
+       const char *key;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               if ((key = ures_getKey(rb->ures))) {
+                       RETVAL_STRING(key, ZSTR_DUPLICATE);
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getString()
+       Returns the contents of a string ResourceBundle */
+static PHP_METHOD(ResourceBundle, getString)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               IF_RB_TYPE(rb, URES_STRING) {
+                       _extract_string(rb->ures, return_value TSRMLS_CC);
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getStringByIndex(int index)
+       Returns the contents of the string at index index in the ResourceBundle 
*/
+static PHP_METHOD(ResourceBundle, getStringByIndex)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       const UChar *str_buf;
+       int32_t str_len;
+       long index;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", 
&index)) {
+               IF_RB_INIT(rb) {
+                       str_buf = ures_getStringByIndex(rb->ures, index, 
&str_len, &status);
+                       IF_RB_SUCCESS(status) {
+                               RETURN_UNICODEL(str_buf, str_len, 
ZSTR_DUPLICATE);
+                       }
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getStringByKey(string key)
+       Returns the contents of the string identified by key in the 
ResourceBundle */
+static PHP_METHOD(ResourceBundle, getStringByKey)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       const UChar *str_buf;
+       int32_t str_len;
+       char *key_str;
+       int key_len;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", 
&key_str, &key_len)) {
+               IF_RB_INIT(rb) {
+                       str_buf = ures_getStringByKey(rb->ures, key_str, 
&str_len, &status);
+                       IF_RB_SUCCESS(status) {
+                               RETURN_UNICODEL(str_buf, str_len, 
ZSTR_DUPLICATE);
+                       }
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto binary ResourceBundle::getBinary(void)
+       Get the binary contents of the ResourceBundle of type BINARY */
+static PHP_METHOD(ResourceBundle, getBinary)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               IF_RB_TYPE(rb, URES_BINARY) {
+                       _extract_binary(rb->ures, return_value TSRMLS_CC);
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto int ResourceBundle::getInt(void)
+       Get the integer value of a ResourceBundle of type INT */
+static PHP_METHOD(ResourceBundle, getInt)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               IF_RB_TYPE(rb, URES_INT) {
+                       _extract_int(rb->ures, return_value TSRMLS_CC);
+               }
+       }
+}
+/* }}} */
+
+/* {{{ proto array ResourceBundle::getArray(void)
+       Get an array of the contents of the ResourceBundle of type ARRAY, TABLE 
or INT_VECTOR*/
+static PHP_METHOD(ResourceBundle, getArray)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               _extract(rb->ures, return_value, 
URES_INT_VECTOR|URES_ARRAY|URES_TABLE TSRMLS_CC);
+       }
+}
+/* }}} */
+
+/* {{{ proto ResourceBundle ResourceBundle::getByIndex(int index)
+       Get a child ResourceBundle by its index */
+static PHP_METHOD(ResourceBundle, getByIndex)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UResourceBundle *ures;
+       long index;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       php_set_error_handling(EH_THROW, NULL TSRMLS_CC);
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", 
&index)) {
+               IF_RB_INIT(rb) {
+                       ures = ures_getByIndex(rb->ures, index, NULL, &status);
+                       IF_RB_SUCCESS(status) {
+                               Z_TYPE_P(return_value) = IS_OBJECT;
+                               Z_OBJVAL_P(return_value) = 
unicode_resourcebundle_create_object_ex(Z_OBJCE_P(getThis()), NULL, ures 
TSRMLS_CC);
+                       }
+               }
+       }
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto ResourceBundle ResourceBundle::getByKey(string key)
+       Get a child ResourceBundle by its key */
+static PHP_METHOD(ResourceBundle, getByKey)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       UResourceBundle *ures;
+       char *key_str;
+       int key_len;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       php_set_error_handling(EH_THROW, NULL TSRMLS_CC);
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", 
&key_str, &key_len)) {
+               IF_RB_INIT(rb) {
+                       ures = ures_getByKey(rb->ures, key_str, NULL, &status);
+                       IF_RB_SUCCESS(status) {
+                               Z_TYPE_P(return_value) = IS_OBJECT;
+                               Z_OBJVAL_P(return_value) = 
unicode_resourcebundle_create_object_ex(Z_OBJCE_P(getThis()), NULL, ures 
TSRMLS_CC);
+                       }
+               }
+       }
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto ResourceBundle ResourceBundle::getByPath(string path[, string 
separator = "/"])
+       Get a child ResourceBundle by specifying it's tree path */
+static PHP_METHOD(ResourceBundle, getByPath)
+{
+       UResourceBundle *ures;
+       char *path_str, *sep_str = NULL;
+       int path_len, sep_len = 0;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       php_set_error_handling(EH_THROW, NULL TSRMLS_CC);
+       if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", 
&path_str, &path_len, &sep_str, &sep_len)) {
+               IF_RB_INIT(rb) {
+                       if (SUCCESS == 
unicode_resourcebundle_walktree(rb->ures, &ures, path_str, path_len, sep_len ? 
*sep_str : '/' TSRMLS_CC)) {
+                               Z_TYPE_P(return_value) = IS_OBJECT;
+                               Z_OBJVAL_P(return_value) = 
unicode_resourcebundle_create_object_ex(Z_OBJCE_P(getThis()), NULL, ures 
TSRMLS_CC);
+                       }
+               }
+       }
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto mixed ResourceBundle::get()
+       Get ResourceBundles contents dependent on its type */
+static PHP_METHOD(ResourceBundle, get)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               _extract(rb->ures, return_value, _EXTRACT_ALL TSRMLS_CC);
+       }
+}
+/* }}} */
+
+/* {{{ proto int ResourceBundle::count(void) */
+static PHP_METHOD(ResourceBundle, count)
+{
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "");
+       
+       IF_RB_INIT(rb) {
+               RETVAL_LONG(ures_getSize(rb->ures));
+       }
+}
+/* }}} */
+
+/* {{{ proto string ResourceBundle::getCat(int section, int message[, string 
default])
+       Get a string of a message catalog located in a ResourceBundle */
+static PHP_METHOD(ResourceBundle, getCat)
+{
+       UErrorCode status = U_ZERO_ERROR;
+       const UChar *real_str;
+       int32_t real_len;
+       UChar *default_str = (UChar *) "\0\0";
+       int default_len = 0;
+       long sect_id, msg_id;
+       php_resourcebundle *rb = zend_object_store_get_object(getThis() 
TSRMLS_CC);
+       
+       if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|u", 
&sect_id, &msg_id, &default_str, &default_len)) {
+               RETURN_FALSE;
+       }
+       
+       IF_RB_INIT(rb) {
+               real_len = default_len;
+               real_str = u_catgets(rb->ures, sect_id, msg_id, default_str, 
&real_len, &status);
+               IF_RB_SUCCESS(status) {
+                       if (real_str) {
+                               RETURN_UNICODEL(real_str, real_len, 
ZSTR_DUPLICATE);
+                       }
+               }
+       }
+       
+       if (default_str) {
+               RETURN_UNICODEL(default_str, default_len, ZSTR_DUPLICATE);
+       } else {
+               RETURN_EMPTY_UNICODE();
+       }
+}
+/* }}} */
+
+/* # */
+
+/* {{{ php_init_resourcebundle */
+void php_init_resourcebundle(TSRMLS_D)
+{
+       zend_class_entry ce;
+       
+       memset(&ce, 0, sizeof(ce));
+       INIT_CLASS_ENTRY(ce, "ResourceBundle", unicode_resourcebundle_fe);
+       ce.create_object = unicode_resourcebundle_create_object;
+       ce.get_iterator = unicode_resourcebundle_get_iterator;
+       
+       unicode_resourcebundle_ce = zend_register_internal_class_ex(&ce, NULL, 
NULL TSRMLS_CC);
+       /* TODO: implements Countable */
+       memcpy(&unicode_resourcebundle_oh, zend_get_std_object_handlers(), 
sizeof(unicode_resourcebundle_oh));
+       unicode_resourcebundle_oh.clone_obj = NULL;
+       unicode_resourcebundle_oh.unset_dimension = NULL;
+       unicode_resourcebundle_oh.write_dimension = NULL;
+       unicode_resourcebundle_oh.read_dimension = 
unicode_resourcebundle_read_dimension;
+       unicode_resourcebundle_oh.has_dimension = 
unicode_resourcebundle_has_dimension;
+       unicode_resourcebundle_oh.cast_object = 
unicode_resourcebundle_cast_object;
+       unicode_resourcebundle_oh.get_properties = 
unicode_resourcebundle_get_properties;
+       
+       /* any warning code of ures_open() */
+       zend_declare_property_long(unicode_resourcebundle_ce, 
ZEND_STRL("warning"), U_ZERO_ERROR, ZEND_ACC_PUBLIC TSRMLS_CC);
+       
+       /* type constants are in constants.c*/
+}
+/* }}} */
+
+/*
+ * 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
+ */
Index: ext/unicode/unicode.c
===================================================================
RCS file: /repository/php-src/ext/unicode/unicode.c,v
retrieving revision 1.42
diff -u -p -d -r1.42 unicode.c
--- ext/unicode/unicode.c       21 Sep 2006 19:30:33 -0000      1.42
+++ ext/unicode/unicode.c       14 Dec 2006 23:10:29 -0000
@@ -399,6 +399,7 @@ PHP_MINIT_FUNCTION(unicode)
 {
        php_register_unicode_iterators(TSRMLS_C);
        php_init_collation(TSRMLS_C);
+       php_init_resourcebundle(TSRMLS_C);
        php_register_unicode_constants(TSRMLS_C);
        
        return SUCCESS;
Index: ext/unicode/tests/resourcebundle_001.phpt
===================================================================
RCS file: ext/unicode/tests/resourcebundle_001.phpt
diff -N ext/unicode/tests/resourcebundle_001.phpt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ext/unicode/tests/resourcebundle_001.phpt   14 Dec 2006 23:10:29 -0000
@@ -0,0 +1,65 @@
+--TEST--
+Unicode: ResourceBundle
+--FILE--
+<?php
+$path = dirname(__FILE__)."/test";
+$rb = new ResourceBundle(dirname(__FILE__)."/test");
+var_dump($rb->getByIndex(0)->getString());
+var_dump($rb->getStringByIndex(0));
+var_dump($rb->getByKey("serial")->getArray());
+echo "--\n";
+foreach (new ResourceBundle($path) as $k => $r) var_dump($k, $r->getKey(), 
$r->get());
+?>
+--EXPECT--
+unicode(5) "value"
+unicode(5) "value"
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(3)
+  [2]=>
+  int(5)
+}
+--
+string(4) "data"
+string(4) "data"
+unicode(5) "value"
+string(5) "greet"
+string(5) "greet"
+array(7) {
+  ["1%4"]=>
+  unicode(13) "Good morning."
+  ["1%5"]=>
+  unicode(15) "Good afternoon."
+  ["1%7"]=>
+  unicode(13) "Good evening."
+  ["1%8"]=>
+  unicode(11) "Good night."
+  ["4%14"]=>
+  unicode(7) "Please "
+  ["4%19"]=>
+  unicode(10) "Thank you."
+  ["4%20"]=>
+  unicode(10) "Sincerely,"
+}
+string(4) "list"
+string(4) "list"
+array(3) {
+  ["one"]=>
+  int(1)
+  ["three"]=>
+  string(1) "4"
+  ["two"]=>
+  unicode(1) "2"
+}
+string(6) "serial"
+string(6) "serial"
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(3)
+  [2]=>
+  int(5)
+}
Index: ext/unicode/tests/test_root.res
===================================================================
RCS file: ext/unicode/tests/test_root.res
diff -N ext/unicode/tests/test_root.res
Binary files /dev/null and test_root.res differ
Index: ext/unicode/tests/test_root.txt
===================================================================
RCS file: ext/unicode/tests/test_root.txt
diff -N ext/unicode/tests/test_root.txt
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ ext/unicode/tests/test_root.txt     14 Dec 2006 23:10:29 -0000
@@ -0,0 +1,18 @@
+root { 
+       data:string { "value" }
+       serial:intvector { 1,3,5 }
+       list {
+               one:int { 1 }
+               two { 2 }
+               three:binary { 34 }
+       }
+       greet {
+               1%4  { "Good morning." }
+               1%5  { "Good afternoon." }
+               1%7  { "Good evening." }
+               1%8  { "Good night." }
+               4%14 { "Please " }
+               4%19 { "Thank you." }
+               4%20 { "Sincerely," }
+       }
+}

--- End Message ---
--- Begin Message ---
Hi all,

I'm new to this list, so let me introduce myself: I'm one of the internationalization architects at Yahoo, and have recently started looking into PHP internationalization. I previously worked on Java internationalization at Sun for a few years.

Resource bundles have helped make internationalization of Java applications easy and popular, so I'd like to see a similar capability in PHP. I know gettext is available, but it seems a bit difficult to understand and uses locale specific encodings instead of Unicode.

The ICU style of resource bundles is Unicode based, but also seems more complicated than desirable for PHP. It's designed for a statically typed environment and requires compilation, neither of which fit in well with PHP.

I'd rather start with Java properties files, the simplest and most widely used form of Java resource files. I'd adapt them to PHP 6 by switching their encoding to UTF-8, adopting heredocs, and simplifying their syntax. I'd drop the secondary fallback mechanism, in which resource bundles can inherit individual resources from other bundles. This is an optimization to reduce the size of bundles at the expense of runtime overhead and additional work in creating the bundles. The additional step of finding common resources and moving them to shared bundles is rarely made in normal localization processes, and the space savings don't matter much for PHP, where bundles remain on the server. Dropping the secondary fallback also means that we can simplify the resource bundle to just an array.

Below is my draft specification for a resource bundle mechanism for PHP. For comparison, the specs for the corresponding functionality in Java and ICU4C are:
- ICU resource bundle specification:
    http://icu.sourceforge.net/apiref/icu4c/ures_8h.html
- ICU resource file specification:
http://dev.icu-project.org/cgi-bin/viewcvs.cgi/icuhtml/design/ bnf_rb.txt?view=co
- Java resource bundle specification:
    http://java.sun.com/javase/6/docs/api/java/util/ResourceBundle.html
- Java properties file specification:
http://java.sun.com/javase/6/docs/api/java/util/ Properties.html#load(java.io.Reader)


API:

array intl_get_resources(string base_name)

Returns an array containing the key-value pairs obtained from a resource file. The resource file is looked up for the current locale using the lookup algorithm of section 3.4 of RFC 4647, at each step generating the file name by concatenating the given base name, the string "-", the language tag of the current step, and the string ".pres". The default file name used if no previous step was successful is the concatenation of base name and ".pres". Once a file is found, its contents are interpreted according to the resource file format specified below, and an array is filled with its key-value pairs. An entry with "#locale#" as its key and the actual locale tag of the file found as its value is added to the array, and the array is returned. The function may cache its results, but must check at least once every 60 minutes that the underlying resource files haven't changed.


Resource File Format

- Files are encoded in UTF-8. The first line may be prefixed with a BOM.
- Lines whose first non-whitespace character is "#" are comment lines and are ignored. - Lines that contain only whitespace characters and are not part of a heredoc string are ignored.
- Key-value definitions come in two forms:
o The simple form has a key string, followed by "=", followed by the value, all on one line. The tokens may or may not be surrounded by whitespace characters. Leading and trailing whitespace is trimmed from both key and value. The value cannot start with "<<<"; for values starting with this character sequence, use the heredoc form. o The heredoc form starts with a key string, followed by "=", followed by "<<<", followed by an identifier, all on one line. The tokens may or may not be surrounded by whitespace characters. Leading and trailing whitespace is trimmed from both key and value. The heredoc form ends with a termination line that contains only the identifier, possibly followed by a semicolon. The lines between these two lines, except comment lines, form the heredoc string. The line break before the termination line is removed, all other line breaks are preserved. - Lines that are not comment lines, whitespace lines, or part of a key-value definition are illegal.
- The following escape sequences are recognized in values:
          o "\\" stands for "\"
          o "\n" stands for the newline character, U+000A.
          o "\t" stands for the horizontal tab character, U+0009.
o "\ " stands for the space character, U+0020. This is only needed if the value of a key-value pair starts or ends with a space character. o "\#" stands for the number sign character, U+0023. This is only needed if a line within a heredoc string starts with this character. - A sequence of "\" followed by a character not listed above is illegal. A "\" immediately preceding the end of the file is illegal. - Only the characters horizontal tab, U+0009, and space, U+0020, are considered whitespace.


With that, hello world becomes:

<?php
    $strings = intl_get_resources("strings");
    echo "$strings[hello]";
?>

The strings.pres file contains:
    hello = Hello, world!
and strings-ja.pres contains:
    hello = こんにちは、皆さん。

What do you think?

Regards,
Norbert

--- End Message ---

Reply via email to