Re: [PHP-I18N] [PATCH] ICU ResourceBundles for ext/unicode

[email protected] (Michael Wallner)
Newsgroups php.i18n
Message-ID <[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?

> that said, it would be perfectly fine for allowing access to locale data.
> a few additions i'd make
> 
> 1. allow an indexer as an equivalent to the getByKey() method    e.g. 
> $rb["key"] === $rb->getByIndex("key")
> 2.  cast handlers in addition to getXXX methods

Sounds reasonable, new patch attached.

-- 
Michael
resourcebundle.diff.txt (text/plain, 33.3 KB)
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 13:15:52 -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 13:15:52 -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 13:15:52 -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 13:15:52 -0000
@@ -0,0 +1,908 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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, 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, 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, "Unkown 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, 0 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_factory */
+static int unicode_resourcebundle_factory(INTERNAL_FUNCTION_PARAMETERS)
+{
+	zval *package = NULL, *locale = NULL;
+	UErrorCode status = U_ZERO_ERROR;
+	
+	if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/!z/!", &package, &locale)) {
+		php_resourcebundle *rb = zend_object_store_get_object(getThis() TSRMLS_CC);
+		
+		if (rb->ures) {
+			ures_close(rb->ures);
+		}
+		/* UTODO: use fs encoding */
+		if (package) {
+			switch (Z_TYPE_P(package)) {
+				case IS_NULL:
+				case IS_STRING:
+				case IS_UNICODE:
+					break;
+				default:
+					convert_to_string(package);
+					break;
+			}
+		}
+		if (locale) {
+			switch (Z_TYPE_P(locale)) {
+				case IS_NULL:
+				case IS_STRING:
+					break;
+				default:
+					convert_to_string(locale);
+					break;
+			}
+		}
+		
+		if (package && Z_TYPE_P(package) == IS_UNICODE) {
+			rb->ures = ures_openU(Z_USTRVAL_P(package), locale ? Z_STRVAL_P(locale) : NULL, &status);
+		} else {
+			rb->ures = ures_open(package ? Z_STRVAL_P(package) : NULL, locale ? Z_STRVAL_P(locale) : NULL, &status);
+		}
+		
+		if (U_SUCCESS(status)) {
+			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 && Z_STRVAL_P(package) ? Z_STRVAL_P(package) : "root", locale && Z_STRVAL_P(locale) ? *Z_STRVAL_P(locale) ? Z_STRVAL_P(locale) : "default" : "root");
+		}
+	}
+	return FAILURE;
+}
+/* }}} */
+
+/* {{{ proto void ResourceBundle::__construct([string package[, string locale]])
+	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]])
+	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])
+	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 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 13:15:52 -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 13:15:52 -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 13:15:52 -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," }
+	}
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.