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    | https://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 	return &rb->zend;
71 }
72 /* }}} */
73 
74 /* {{{ ResourceBundle_ctor */
resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS,zend_error_handling * error_handling,bool * error_handling_replaced)75 static int resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_handling, bool *error_handling_replaced)
76 {
77 	const char *bundlename;
78 	size_t		bundlename_len = 0;
79 	const char *locale;
80 	size_t		locale_len = 0;
81 	bool	fallback = 1;
82 
83 	zval                  *object = return_value;
84 	ResourceBundle_object *rb = Z_INTL_RESOURCEBUNDLE_P( object );
85 
86 	intl_error_reset( NULL );
87 
88 	if( zend_parse_parameters( ZEND_NUM_ARGS(), "s!s!|b",
89 		&locale, &locale_len, &bundlename, &bundlename_len, &fallback ) == FAILURE )
90 	{
91 		return FAILURE;
92 	}
93 
94 	if (error_handling != NULL) {
95 		zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, error_handling);
96 		*error_handling_replaced = 1;
97 	}
98 
99 	if (rb->me) {
100 		zend_throw_error(NULL, "ResourceBundle object is already constructed");
101 		return FAILURE;
102 	}
103 
104 	INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len);
105 
106 	if (locale == NULL) {
107 		locale = intl_locale_get_default();
108 	}
109 
110 	if (bundlename_len >= MAXPATHLEN) {
111 		zend_argument_value_error(2, "is too long");
112 		return FAILURE;
113 	}
114 
115 	if (fallback) {
116 		rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
117 	} else {
118 		rb->me = ures_openDirect(bundlename, locale, &INTL_DATA_ERROR_CODE(rb));
119 	}
120 
121 	INTL_CTOR_CHECK_STATUS(rb, "resourcebundle_ctor: Cannot load libICU resource bundle");
122 
123 	if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING ||
124 			INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
125 		char *pbuf;
126 		intl_errors_set_code(NULL, INTL_DATA_ERROR_CODE(rb));
127 		spprintf(&pbuf, 0, "resourcebundle_ctor: Cannot load libICU resource "
128 				"'%s' without fallback from %s to %s",
129 				bundlename ? bundlename : "(default data)", locale,
130 				ures_getLocaleByType(
131 					rb->me, ULOC_ACTUAL_LOCALE, &INTL_DATA_ERROR_CODE(rb)));
132 		intl_errors_set_custom_msg(INTL_DATA_ERROR_P(rb), pbuf, 1);
133 		efree(pbuf);
134 		return FAILURE;
135 	}
136 
137 	return SUCCESS;
138 }
139 /* }}} */
140 
141 /* {{{ ResourceBundle object constructor */
PHP_METHOD(ResourceBundle,__construct)142 PHP_METHOD( ResourceBundle, __construct )
143 {
144 	zend_error_handling error_handling;
145 	bool error_handling_replaced = 0;
146 
147 	return_value = ZEND_THIS;
148 	if (resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, &error_handling, &error_handling_replaced) == FAILURE) {
149 		if (!EG(exception)) {
150 			zend_throw_exception(IntlException_ce_ptr, "Constructor failed", 0);
151 		}
152 	}
153 	if (error_handling_replaced) {
154 		zend_restore_error_handling(&error_handling);
155 	}
156 }
157 /* }}} */
158 
159 /* {{{ */
PHP_FUNCTION(resourcebundle_create)160 PHP_FUNCTION( resourcebundle_create )
161 {
162 	object_init_ex( return_value, ResourceBundle_ce_ptr );
163 	if (resourcebundle_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, NULL) == FAILURE) {
164 		zval_ptr_dtor(return_value);
165 		RETURN_NULL();
166 	}
167 }
168 /* }}} */
169 
170 /* {{{ resourcebundle_array_fetch */
resourcebundle_array_fetch(zend_object * object,zval * offset,zval * return_value,int fallback)171 static void resourcebundle_array_fetch(zend_object *object, zval *offset, zval *return_value, int fallback)
172 {
173 	int32_t     meindex = 0;
174 	char *      mekey = NULL;
175 	bool        is_numeric = 0;
176 	char        *pbuf;
177 	ResourceBundle_object *rb;
178 
179 	rb = php_intl_resourcebundle_fetch_object(object);
180 	intl_error_reset(NULL);
181 	intl_error_reset(INTL_DATA_ERROR_P(rb));
182 
183 	if(Z_TYPE_P(offset) == IS_LONG) {
184 		is_numeric = 1;
185 		meindex = (int32_t)Z_LVAL_P(offset);
186 		rb->child = ures_getByIndex( rb->me, meindex, rb->child, &INTL_DATA_ERROR_CODE(rb) );
187 	} else if(Z_TYPE_P(offset) == IS_STRING) {
188 		mekey = Z_STRVAL_P(offset);
189 		rb->child = ures_getByKey(rb->me, mekey, rb->child, &INTL_DATA_ERROR_CODE(rb) );
190 	} else {
191 		intl_errors_set(INTL_DATA_ERROR_P(rb), U_ILLEGAL_ARGUMENT_ERROR,
192 			"resourcebundle_get: index should be integer or string", 0);
193 		RETURN_NULL();
194 	}
195 
196 	intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) );
197 	if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
198 		if (is_numeric) {
199 			spprintf( &pbuf, 0, "Cannot load resource element %d", meindex );
200 		} else {
201 			spprintf( &pbuf, 0, "Cannot load resource element '%s'", mekey );
202 		}
203 		intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
204 		efree(pbuf);
205 		RETURN_NULL();
206 	}
207 
208 	if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
209 		UErrorCode icuerror;
210 		const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
211 		if (is_numeric) {
212 			spprintf( &pbuf, 0, "Cannot load element %d without fallback from to %s", meindex, locale );
213 		} else {
214 			spprintf( &pbuf, 0, "Cannot load element '%s' without fallback from to %s", mekey, locale );
215 		}
216 		intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
217 		efree(pbuf);
218 		RETURN_NULL();
219 	}
220 
221 	resourcebundle_extract_value( return_value, rb );
222 }
223 /* }}} */
224 
225 /* {{{ resourcebundle_array_get */
resourcebundle_array_get(zend_object * object,zval * offset,int type,zval * rv)226 zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv)
227 {
228 	if(offset == NULL) {
229 		php_error( E_ERROR, "Cannot apply [] to ResourceBundle object" );
230 	}
231 	ZVAL_NULL(rv);
232 	resourcebundle_array_fetch(object, offset, rv, 1);
233 	return rv;
234 }
235 /* }}} */
236 
237 /* {{{ Get resource identified by numerical index or key name. */
PHP_FUNCTION(resourcebundle_get)238 PHP_FUNCTION( resourcebundle_get )
239 {
240 	bool   fallback = 1;
241 	zval *		offset;
242 	zval *      object;
243 
244 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oz|b",	&object, ResourceBundle_ce_ptr, &offset, &fallback ) == FAILURE) {
245 		RETURN_THROWS();
246 	}
247 
248 	resourcebundle_array_fetch(Z_OBJ_P(object), offset, return_value, fallback);
249 }
250 /* }}} */
251 
252 /* {{{ resourcebundle_array_count */
resourcebundle_array_count(zend_object * object,zend_long * count)253 static zend_result resourcebundle_array_count(zend_object *object, zend_long *count)
254 {
255 	ResourceBundle_object *rb = php_intl_resourcebundle_fetch_object(object);
256 
257 	if (rb->me == NULL) {
258 		intl_errors_set(&rb->error, U_ILLEGAL_ARGUMENT_ERROR,
259 				"Found unconstructed ResourceBundle", 0);
260 		return 0;
261 	}
262 
263 	*count = ures_getSize( rb->me );
264 
265 	return SUCCESS;
266 }
267 /* }}} */
268 
269 /* {{{ Get resources count */
PHP_FUNCTION(resourcebundle_count)270 PHP_FUNCTION( resourcebundle_count )
271 {
272 	int32_t                len;
273 	RESOURCEBUNDLE_METHOD_INIT_VARS;
274 
275 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O", &object, ResourceBundle_ce_ptr ) == FAILURE ) {
276 		RETURN_THROWS();
277 	}
278 
279 	RESOURCEBUNDLE_METHOD_FETCH_OBJECT;
280 
281 	len = ures_getSize( rb->me );
282 	RETURN_LONG( len );
283 }
284 
285 /* {{{ Get available locales from ResourceBundle name */
PHP_FUNCTION(resourcebundle_locales)286 PHP_FUNCTION( resourcebundle_locales )
287 {
288 	char * bundlename;
289 	size_t    bundlename_len = 0;
290 	const char * entry;
291 	int entry_len;
292 	UEnumeration *icuenum;
293 	UErrorCode   icuerror = U_ZERO_ERROR;
294 
295 	intl_errors_reset( NULL );
296 
297 	if( zend_parse_parameters(ZEND_NUM_ARGS(), "s", &bundlename, &bundlename_len ) == FAILURE )
298 	{
299 		RETURN_THROWS();
300 	}
301 
302 	if (bundlename_len >= MAXPATHLEN) {
303 		zend_argument_value_error(1, "is too long");
304 		RETURN_THROWS();
305 	}
306 
307 	if(bundlename_len == 0) {
308 		// fetch default locales list
309 		bundlename = NULL;
310 	}
311 
312 	icuenum = ures_openAvailableLocales( bundlename, &icuerror );
313 	INTL_CHECK_STATUS(icuerror, "Cannot fetch locales list");
314 
315 	uenum_reset( icuenum, &icuerror );
316 	INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list");
317 
318 	array_init( return_value );
319 	while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) {
320 		add_next_index_stringl( return_value, (char *) entry, entry_len);
321 	}
322 	uenum_close( icuenum );
323 }
324 /* }}} */
325 
326 /* {{{ Get text description for ResourceBundle's last error code. */
PHP_FUNCTION(resourcebundle_get_error_code)327 PHP_FUNCTION( resourcebundle_get_error_code )
328 {
329 	RESOURCEBUNDLE_METHOD_INIT_VARS;
330 
331 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
332 		&object, ResourceBundle_ce_ptr ) == FAILURE )
333 	{
334 		RETURN_THROWS();
335 	}
336 
337 	rb = Z_INTL_RESOURCEBUNDLE_P( object );
338 
339 	RETURN_LONG(INTL_DATA_ERROR_CODE(rb));
340 }
341 /* }}} */
342 
343 /* {{{ Get text description for ResourceBundle's last error. */
PHP_FUNCTION(resourcebundle_get_error_message)344 PHP_FUNCTION( resourcebundle_get_error_message )
345 {
346 	zend_string* message = NULL;
347 	RESOURCEBUNDLE_METHOD_INIT_VARS;
348 
349 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
350 		&object, ResourceBundle_ce_ptr ) == FAILURE )
351 	{
352 		RETURN_THROWS();
353 	}
354 
355 	rb = Z_INTL_RESOURCEBUNDLE_P( object );
356 	message = intl_error_get_message(INTL_DATA_ERROR_P(rb));
357 	RETURN_STR(message);
358 }
359 /* }}} */
360 
PHP_METHOD(ResourceBundle,getIterator)361 PHP_METHOD(ResourceBundle, getIterator) {
362 	if (zend_parse_parameters_none() == FAILURE) {
363 		return;
364 	}
365 
366 	zend_create_internal_iterator_zval(return_value, ZEND_THIS);
367 }
368 
369 /* {{{ resourcebundle_register_class
370  * Initialize 'ResourceBundle' class
371  */
resourcebundle_register_class(void)372 void resourcebundle_register_class( void )
373 {
374 	ResourceBundle_ce_ptr = register_class_ResourceBundle(zend_ce_aggregate, zend_ce_countable);
375 	ResourceBundle_ce_ptr->create_object = ResourceBundle_object_create;
376 	ResourceBundle_ce_ptr->default_object_handlers = &ResourceBundle_object_handlers;
377 	ResourceBundle_ce_ptr->get_iterator = resourcebundle_get_iterator;
378 
379 	ResourceBundle_object_handlers = std_object_handlers;
380 	ResourceBundle_object_handlers.offset = XtOffsetOf(ResourceBundle_object, zend);
381 	ResourceBundle_object_handlers.clone_obj	  = NULL; /* ICU ResourceBundle has no clone implementation */
382 	ResourceBundle_object_handlers.free_obj = ResourceBundle_object_free;
383 	ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get;
384 	ResourceBundle_object_handlers.count_elements = resourcebundle_array_count;
385 }
386 /* }}} */
387