1 /*
2    +----------------------------------------------------------------------+
3    | This source file is subject to version 3.01 of the PHP license,      |
4    | that is bundled with this package in the file LICENSE, and is        |
5    | available through the world-wide-web at the following url:           |
6    | http://www.php.net/license/3_01.txt                                  |
7    | If you did not receive a copy of the PHP license and are unable to   |
8    | obtain it through the world-wide-web, please send a note to          |
9    | license@php.net so we can mail you a copy immediately.               |
10    +----------------------------------------------------------------------+
11    | Authors: Hans-Peter Oeri (University of St.Gallen) <hp@oeri.ch>      |
12    +----------------------------------------------------------------------+
13  */
14 
15 #include <stdlib.h>
16 #include <unicode/ures.h>
17 #include <unicode/uenum.h>
18 
19 #include <zend.h>
20 #include <Zend/zend_exceptions.h>
21 #include <Zend/zend_interfaces.h>
22 #include <php.h>
23 
24 #include "php_intl.h"
25 #include "intl_data.h"
26 #include "intl_common.h"
27 
28 #include "resourcebundle/resourcebundle.h"
29 #include "resourcebundle/resourcebundle_iterator.h"
30 #include "resourcebundle/resourcebundle_class.h"
31 #include "resourcebundle/resourcebundle_arginfo.h"
32 
33 zend_class_entry *ResourceBundle_ce_ptr = NULL;
34 
35 static zend_object_handlers ResourceBundle_object_handlers;
36 
37 /* {{{ ResourceBundle_object_free */
ResourceBundle_object_free(zend_object * object)38 static void ResourceBundle_object_free( zend_object *object )
39 {
40 	ResourceBundle_object *rb = php_intl_resourcebundle_fetch_object(object);
41 
42 	// only free local errors
43 	intl_error_reset( INTL_DATA_ERROR_P(rb) );
44 
45 	if (rb->me) {
46 		ures_close( rb->me );
47 	}
48 	if (rb->child) {
49 		ures_close( rb->child );
50 	}
51 
52 	zend_object_std_dtor( &rb->zend );
53 }
54 /* }}} */
55 
56 /* {{{ ResourceBundle_object_create */
ResourceBundle_object_create(zend_class_entry * ce)57 static zend_object *ResourceBundle_object_create( zend_class_entry *ce )
58 {
59 	ResourceBundle_object *rb;
60 
61 	rb = zend_object_alloc(sizeof(ResourceBundle_object), ce);
62 
63 	zend_object_std_init( &rb->zend, ce );
64 	object_properties_init( &rb->zend, ce);
65 
66 	intl_error_init( INTL_DATA_ERROR_P(rb) );
67 	rb->me = NULL;
68 	rb->child = NULL;
69 
70 	rb->zend.handlers = &ResourceBundle_object_handlers;
71 
72 	return &rb->zend;
73 }
74 /* }}} */
75 
76 /* {{{ ResourceBundle_ctor */
resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS)77 static int resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS)
78 {
79 	const char *bundlename;
80 	size_t		bundlename_len = 0;
81 	const char *locale;
82 	size_t		locale_len = 0;
83 	zend_bool	fallback = 1;
84 
85 	zval                  *object = return_value;
86 	ResourceBundle_object *rb = Z_INTL_RESOURCEBUNDLE_P( object );
87 
88 	intl_error_reset( NULL );
89 
90 	if( zend_parse_parameters( ZEND_NUM_ARGS(), "s!s!|b",
91 		&locale, &locale_len, &bundlename, &bundlename_len, &fallback ) == FAILURE )
92 	{
93 		return FAILURE;
94 	}
95 
96 	if (rb->me) {
97 		zend_throw_error(NULL, "ResourceBundle object is already constructed");
98 		return FAILURE;
99 	}
100 
101 	INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len);
102 
103 	if (locale == NULL) {
104 		locale = intl_locale_get_default();
105 	}
106 
107 	if (bundlename_len >= MAXPATHLEN) {
108 		zend_argument_value_error(2, "is too long");
109 		return FAILURE;
110 	}
111 
112 	if (fallback) {
113 		rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
114 	} else {
115 		rb->me = ures_openDirect(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
116 	}
117 
118 	INTL_CTOR_CHECK_STATUS(rb, "resourcebundle_ctor: Cannot load libICU resource bundle");
119 
120 	if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING ||
121 			INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
122 		char *pbuf;
123 		intl_errors_set_code(NULL, INTL_DATA_ERROR_CODE(rb));
124 		spprintf(&pbuf, 0, "resourcebundle_ctor: Cannot load libICU resource "
125 				"'%s' without fallback from %s to %s",
126 				bundlename ? bundlename : "(default data)", locale,
127 				ures_getLocaleByType(
128 					rb->me, ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(rb)));
129 		intl_errors_set_custom_msg(INTL_DATA_ERROR_P(rb), pbuf, 1);
130 		efree(pbuf);
131 		return FAILURE;
132 	}
133 
134 	return SUCCESS;
135 }
136 /* }}} */
137 
138 /* {{{ ResourceBundle object constructor */
PHP_METHOD(ResourceBundle,__construct)139 PHP_METHOD( ResourceBundle, __construct )
140 {
141 	zend_error_handling error_handling;
142 
143 	zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);
144 	return_value = ZEND_THIS;
145 	if (resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU) == FAILURE) {
146 		if (!EG(exception)) {
147 			zend_throw_exception(IntlException_ce_ptr, "Constructor failed", 0);
148 		}
149 	}
150 	zend_restore_error_handling(&error_handling);
151 }
152 /* }}} */
153 
154 /* {{{ */
PHP_FUNCTION(resourcebundle_create)155 PHP_FUNCTION( resourcebundle_create )
156 {
157 	object_init_ex( return_value, ResourceBundle_ce_ptr );
158 	if (resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU) == FAILURE) {
159 		zval_ptr_dtor(return_value);
160 		RETURN_NULL();
161 	}
162 }
163 /* }}} */
164 
165 /* {{{ resourcebundle_array_fetch */
resourcebundle_array_fetch(zend_object * object,zval * offset,zval * return_value,int fallback)166 static void resourcebundle_array_fetch(zend_object *object, zval *offset, zval *return_value, int fallback)
167 {
168 	int32_t     meindex = 0;
169 	char *      mekey = NULL;
170     zend_bool    is_numeric = 0;
171 	char         *pbuf;
172 	ResourceBundle_object *rb;
173 
174 	rb = php_intl_resourcebundle_fetch_object(object);
175 	intl_error_reset(NULL);
176 	intl_error_reset(INTL_DATA_ERROR_P(rb));
177 
178 	if(Z_TYPE_P(offset) == IS_LONG) {
179 		is_numeric = 1;
180 		meindex = (int32_t)Z_LVAL_P(offset);
181 		rb->child = ures_getByIndex( rb->me, meindex, rb->child, &INTL_DATA_ERROR_CODE(rb) );
182 	} else if(Z_TYPE_P(offset) == IS_STRING) {
183 		mekey = Z_STRVAL_P(offset);
184 		rb->child = ures_getByKey(rb->me, mekey, rb->child, &INTL_DATA_ERROR_CODE(rb) );
185 	} else {
186 		intl_errors_set(INTL_DATA_ERROR_P(rb), U_ILLEGAL_ARGUMENT_ERROR,
187 			"resourcebundle_get: index should be integer or string", 0);
188 		RETURN_NULL();
189 	}
190 
191 	intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) );
192 	if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
193 		if (is_numeric) {
194 			spprintf( &pbuf, 0, "Cannot load resource element %d", meindex );
195 		} else {
196 			spprintf( &pbuf, 0, "Cannot load resource element '%s'", mekey );
197 		}
198 		intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
199 		efree(pbuf);
200 		RETURN_NULL();
201 	}
202 
203 	if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
204 		UErrorCode icuerror;
205 		const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
206 		if (is_numeric) {
207 			spprintf( &pbuf, 0, "Cannot load element %d without fallback from to %s", meindex, locale );
208 		} else {
209 			spprintf( &pbuf, 0, "Cannot load element '%s' without fallback from to %s", mekey, locale );
210 		}
211 		intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
212 		efree(pbuf);
213 		RETURN_NULL();
214 	}
215 
216 	resourcebundle_extract_value( return_value, rb );
217 }
218 /* }}} */
219 
220 /* {{{ resourcebundle_array_get */
resourcebundle_array_get(zend_object * object,zval * offset,int type,zval * rv)221 zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv)
222 {
223 	if(offset == NULL) {
224 		php_error( E_ERROR, "Cannot apply [] to ResourceBundle object" );
225 	}
226 	ZVAL_NULL(rv);
227 	resourcebundle_array_fetch(object, offset, rv, 1);
228 	return rv;
229 }
230 /* }}} */
231 
232 /* {{{ Get resource identified by numerical index or key name. */
PHP_FUNCTION(resourcebundle_get)233 PHP_FUNCTION( resourcebundle_get )
234 {
235 	zend_bool   fallback = 1;
236 	zval *		offset;
237 	zval *      object;
238 
239 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz|b",	&object, ResourceBundle_ce_ptr, &offset, &fallback ) == FAILURE) {
240 		RETURN_THROWS();
241 	}
242 
243 	resourcebundle_array_fetch(Z_OBJ_P(object), offset, return_value, fallback);
244 }
245 /* }}} */
246 
247 /* {{{ resourcebundle_array_count */
resourcebundle_array_count(zend_object * object,zend_long * count)248 int resourcebundle_array_count(zend_object *object, zend_long *count)
249 {
250 	ResourceBundle_object *rb = php_intl_resourcebundle_fetch_object(object);
251 
252 	if (rb->me == NULL) {
253 		intl_errors_set(&rb->error, U_ILLEGAL_ARGUMENT_ERROR,
254 				"Found unconstructed ResourceBundle", 0);
255 		return 0;
256 	}
257 
258 	*count = ures_getSize( rb->me );
259 
260 	return SUCCESS;
261 }
262 /* }}} */
263 
264 /* {{{ Get resources count */
PHP_FUNCTION(resourcebundle_count)265 PHP_FUNCTION( resourcebundle_count )
266 {
267 	int32_t                len;
268 	RESOURCEBUNDLE_METHOD_INIT_VARS;
269 
270 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O", &object, ResourceBundle_ce_ptr ) == FAILURE ) {
271 		RETURN_THROWS();
272 	}
273 
274 	RESOURCEBUNDLE_METHOD_FETCH_OBJECT;
275 
276 	len = ures_getSize( rb->me );
277 	RETURN_LONG( len );
278 }
279 
280 /* {{{ Get available locales from ResourceBundle name */
PHP_FUNCTION(resourcebundle_locales)281 PHP_FUNCTION( resourcebundle_locales )
282 {
283 	char * bundlename;
284 	size_t    bundlename_len = 0;
285 	const char * entry;
286 	int entry_len;
287 	UEnumeration *icuenum;
288 	UErrorCode   icuerror = U_ZERO_ERROR;
289 
290 	intl_errors_reset( NULL );
291 
292 	if( zend_parse_parameters(ZEND_NUM_ARGS(), "s", &bundlename, &bundlename_len ) == FAILURE )
293 	{
294 		RETURN_THROWS();
295 	}
296 
297 	if (bundlename_len >= MAXPATHLEN) {
298 		zend_argument_value_error(1, "is too long");
299 		RETURN_THROWS();
300 	}
301 
302 	if(bundlename_len == 0) {
303 		// fetch default locales list
304 		bundlename = NULL;
305 	}
306 
307 	icuenum = ures_openAvailableLocales( bundlename, &icuerror );
308 	INTL_CHECK_STATUS(icuerror, "Cannot fetch locales list");
309 
310 	uenum_reset( icuenum, &icuerror );
311 	INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list");
312 
313 	array_init( return_value );
314 	while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) {
315 		add_next_index_stringl( return_value, (char *) entry, entry_len);
316 	}
317 	uenum_close( icuenum );
318 }
319 /* }}} */
320 
321 /* {{{ Get text description for ResourceBundle's last error code. */
PHP_FUNCTION(resourcebundle_get_error_code)322 PHP_FUNCTION( resourcebundle_get_error_code )
323 {
324 	RESOURCEBUNDLE_METHOD_INIT_VARS;
325 
326 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
327 		&object, ResourceBundle_ce_ptr ) == FAILURE )
328 	{
329 		RETURN_THROWS();
330 	}
331 
332 	rb = Z_INTL_RESOURCEBUNDLE_P( object );
333 
334 	RETURN_LONG(INTL_DATA_ERROR_CODE(rb));
335 }
336 /* }}} */
337 
338 /* {{{ Get text description for ResourceBundle's last error. */
PHP_FUNCTION(resourcebundle_get_error_message)339 PHP_FUNCTION( resourcebundle_get_error_message )
340 {
341 	zend_string* message = NULL;
342 	RESOURCEBUNDLE_METHOD_INIT_VARS;
343 
344 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
345 		&object, ResourceBundle_ce_ptr ) == FAILURE )
346 	{
347 		RETURN_THROWS();
348 	}
349 
350 	rb = Z_INTL_RESOURCEBUNDLE_P( object );
351 	message = intl_error_get_message(INTL_DATA_ERROR_P(rb));
352 	RETURN_STR(message);
353 }
354 /* }}} */
355 
PHP_METHOD(ResourceBundle,getIterator)356 PHP_METHOD(ResourceBundle, getIterator) {
357 	if (zend_parse_parameters_none() == FAILURE) {
358 		return;
359 	}
360 
361 	zend_create_internal_iterator_zval(return_value, ZEND_THIS);
362 }
363 
364 /* {{{ resourcebundle_register_class
365  * Initialize 'ResourceBundle' class
366  */
resourcebundle_register_class(void)367 void resourcebundle_register_class( void )
368 {
369 	zend_class_entry ce;
370 
371 	INIT_CLASS_ENTRY( ce, "ResourceBundle", class_ResourceBundle_methods );
372 
373 	ce.create_object = ResourceBundle_object_create;
374 	ce.get_iterator = resourcebundle_get_iterator;
375 
376 	ResourceBundle_ce_ptr = zend_register_internal_class( &ce );
377 
378 	ResourceBundle_object_handlers = std_object_handlers;
379 	ResourceBundle_object_handlers.offset = XtOffsetOf(ResourceBundle_object, zend);
380 	ResourceBundle_object_handlers.clone_obj	  = NULL; /* ICU ResourceBundle has no clone implementation */
381 	ResourceBundle_object_handlers.free_obj = ResourceBundle_object_free;
382 	ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get;
383 	ResourceBundle_object_handlers.count_elements = resourcebundle_array_count;
384 
385 	zend_class_implements(ResourceBundle_ce_ptr, 2, zend_ce_aggregate, zend_ce_countable);
386 }
387 /* }}} */
388