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 */
resource_bundle_array_fetch(zend_object * object,zend_string * offset_str,zend_long offset_int,zval * return_value,bool fallback,uint32_t offset_arg_num)171 static zval *resource_bundle_array_fetch(
172 zend_object *object, zend_string *offset_str, zend_long offset_int,
173 zval *return_value, bool fallback, uint32_t offset_arg_num)
174 {
175 int32_t index = 0;
176 char *key = NULL;
177 bool is_numeric = offset_str == NULL;
178 char *pbuf;
179 ResourceBundle_object *rb;
180
181 rb = php_intl_resourcebundle_fetch_object(object);
182 intl_error_reset(NULL);
183 intl_error_reset(INTL_DATA_ERROR_P(rb));
184
185 if (offset_str) {
186 if (UNEXPECTED(ZSTR_LEN(offset_str) == 0)) {
187 if (offset_arg_num) {
188 zend_argument_value_error(offset_arg_num, "cannot be empty");
189 } else {
190 zend_value_error("Offset cannot be empty");
191 }
192 return NULL;
193 }
194 key = ZSTR_VAL(offset_str);
195 rb->child = ures_getByKey(rb->me, key, rb->child, &INTL_DATA_ERROR_CODE(rb) );
196 } else {
197 if (UNEXPECTED(offset_int < (zend_long)INT32_MIN || offset_int > (zend_long)INT32_MAX)) {
198 if (offset_arg_num) {
199 zend_argument_value_error(offset_arg_num, "index must be between %d and %d", INT32_MIN, INT32_MAX);
200 } else {
201 zend_value_error("Index must be between %d and %d", INT32_MIN, INT32_MAX);
202 }
203 return NULL;
204 }
205 index = (int32_t)offset_int;
206 rb->child = ures_getByIndex(rb->me, index, rb->child, &INTL_DATA_ERROR_CODE(rb));
207 }
208
209 intl_error_set_code( NULL, INTL_DATA_ERROR_CODE(rb) );
210 if (U_FAILURE(INTL_DATA_ERROR_CODE(rb))) {
211 if (is_numeric) {
212 spprintf( &pbuf, 0, "Cannot load resource element %d", index );
213 } else {
214 spprintf( &pbuf, 0, "Cannot load resource element '%s'", key );
215 }
216 intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1 );
217 efree(pbuf);
218 RETVAL_NULL();
219 return return_value;
220 }
221
222 if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) {
223 UErrorCode icuerror;
224 const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror );
225 if (is_numeric) {
226 spprintf(&pbuf, 0, "Cannot load element %d without fallback from to %s", index, locale);
227 } else {
228 spprintf(&pbuf, 0, "Cannot load element '%s' without fallback from to %s", key, locale);
229 }
230 intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf, 1);
231 efree(pbuf);
232 RETVAL_NULL();
233 return return_value;
234 }
235
236 resourcebundle_extract_value( return_value, rb );
237 return return_value;
238 }
239 /* }}} */
240
241 /* {{{ resourcebundle_array_get */
resourcebundle_array_get(zend_object * object,zval * offset,int type,zval * rv)242 zval *resourcebundle_array_get(zend_object *object, zval *offset, int type, zval *rv)
243 {
244 if (offset == NULL) {
245 zend_throw_error(NULL, "Cannot apply [] to ResourceBundle object");
246 return NULL;
247 }
248
249 ZVAL_DEREF(offset);
250 if (Z_TYPE_P(offset) == IS_LONG) {
251 return resource_bundle_array_fetch(object, /* offset_str */ NULL, Z_LVAL_P(offset), rv, /* fallback */ true, /* arg_num */ 0);
252 } else if (Z_TYPE_P(offset) == IS_STRING) {
253 return resource_bundle_array_fetch(object, Z_STR_P(offset), /* offset_int */ 0, rv, /* fallback */ true, /* arg_num */ 0);
254 } else {
255 zend_illegal_container_offset(object->ce->name, offset, type);
256 return NULL;
257 }
258 }
259 /* }}} */
260
261 /* {{{ Get resource identified by numerical index or key name. */
PHP_FUNCTION(resourcebundle_get)262 PHP_FUNCTION( resourcebundle_get )
263 {
264 bool fallback = true;
265 zend_object *resource_bundle = NULL;
266 zend_string *offset_str = NULL;
267 zend_long offset_long = 0;
268
269 ZEND_PARSE_PARAMETERS_START(2, 3)
270 Z_PARAM_OBJ_OF_CLASS(resource_bundle, ResourceBundle_ce_ptr)
271 Z_PARAM_STR_OR_LONG(offset_str, offset_long)
272 Z_PARAM_OPTIONAL
273 Z_PARAM_BOOL(fallback)
274 ZEND_PARSE_PARAMETERS_END();
275
276 zval *retval = resource_bundle_array_fetch(resource_bundle, offset_str, offset_long, return_value, fallback, /* arg_num */ 2);
277 if (!retval) {
278 RETURN_THROWS();
279 }
280 }
281 /* }}} */
282
PHP_METHOD(ResourceBundle,get)283 PHP_METHOD(ResourceBundle , get)
284 {
285 bool fallback = true;
286 zend_string *offset_str = NULL;
287 zend_long offset_long = 0;
288
289 ZEND_PARSE_PARAMETERS_START(1, 2)
290 Z_PARAM_STR_OR_LONG(offset_str, offset_long)
291 Z_PARAM_OPTIONAL
292 Z_PARAM_BOOL(fallback)
293 ZEND_PARSE_PARAMETERS_END();
294
295 zval *retval = resource_bundle_array_fetch(Z_OBJ_P(ZEND_THIS), offset_str, offset_long, return_value, fallback, /* arg_num */ 1);
296 if (!retval) {
297 RETURN_THROWS();
298 }
299 }
300 /* }}} */
301
302 /* {{{ resourcebundle_array_count */
resourcebundle_array_count(zend_object * object,zend_long * count)303 static zend_result resourcebundle_array_count(zend_object *object, zend_long *count)
304 {
305 ResourceBundle_object *rb = php_intl_resourcebundle_fetch_object(object);
306
307 if (rb->me == NULL) {
308 intl_errors_set(&rb->error, U_ILLEGAL_ARGUMENT_ERROR,
309 "Found unconstructed ResourceBundle", 0);
310 return 0;
311 }
312
313 *count = ures_getSize( rb->me );
314
315 return SUCCESS;
316 }
317 /* }}} */
318
319 /* {{{ Get resources count */
PHP_FUNCTION(resourcebundle_count)320 PHP_FUNCTION( resourcebundle_count )
321 {
322 int32_t len;
323 RESOURCEBUNDLE_METHOD_INIT_VARS;
324
325 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O", &object, ResourceBundle_ce_ptr ) == FAILURE ) {
326 RETURN_THROWS();
327 }
328
329 RESOURCEBUNDLE_METHOD_FETCH_OBJECT;
330
331 len = ures_getSize( rb->me );
332 RETURN_LONG( len );
333 }
334
335 /* {{{ Get available locales from ResourceBundle name */
PHP_FUNCTION(resourcebundle_locales)336 PHP_FUNCTION( resourcebundle_locales )
337 {
338 char * bundlename;
339 size_t bundlename_len = 0;
340 const char * entry;
341 int entry_len;
342 UEnumeration *icuenum;
343 UErrorCode icuerror = U_ZERO_ERROR;
344
345 intl_errors_reset( NULL );
346
347 if( zend_parse_parameters(ZEND_NUM_ARGS(), "s", &bundlename, &bundlename_len ) == FAILURE )
348 {
349 RETURN_THROWS();
350 }
351
352 if (bundlename_len >= MAXPATHLEN) {
353 zend_argument_value_error(1, "is too long");
354 RETURN_THROWS();
355 }
356
357 if(bundlename_len == 0) {
358 // fetch default locales list
359 bundlename = NULL;
360 }
361
362 icuenum = ures_openAvailableLocales( bundlename, &icuerror );
363 INTL_CHECK_STATUS(icuerror, "Cannot fetch locales list");
364
365 uenum_reset( icuenum, &icuerror );
366 INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list");
367
368 array_init( return_value );
369 while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) {
370 add_next_index_stringl( return_value, (char *) entry, entry_len);
371 }
372 uenum_close( icuenum );
373 }
374 /* }}} */
375
376 /* {{{ Get text description for ResourceBundle's last error code. */
PHP_FUNCTION(resourcebundle_get_error_code)377 PHP_FUNCTION( resourcebundle_get_error_code )
378 {
379 RESOURCEBUNDLE_METHOD_INIT_VARS;
380
381 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
382 &object, ResourceBundle_ce_ptr ) == FAILURE )
383 {
384 RETURN_THROWS();
385 }
386
387 rb = Z_INTL_RESOURCEBUNDLE_P( object );
388
389 RETURN_LONG(INTL_DATA_ERROR_CODE(rb));
390 }
391 /* }}} */
392
393 /* {{{ Get text description for ResourceBundle's last error. */
PHP_FUNCTION(resourcebundle_get_error_message)394 PHP_FUNCTION( resourcebundle_get_error_message )
395 {
396 zend_string* message = NULL;
397 RESOURCEBUNDLE_METHOD_INIT_VARS;
398
399 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
400 &object, ResourceBundle_ce_ptr ) == FAILURE )
401 {
402 RETURN_THROWS();
403 }
404
405 rb = Z_INTL_RESOURCEBUNDLE_P( object );
406 message = intl_error_get_message(INTL_DATA_ERROR_P(rb));
407 RETURN_STR(message);
408 }
409 /* }}} */
410
PHP_METHOD(ResourceBundle,getIterator)411 PHP_METHOD(ResourceBundle, getIterator) {
412 if (zend_parse_parameters_none() == FAILURE) {
413 return;
414 }
415
416 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
417 }
418
419 /* {{{ resourcebundle_register_class
420 * Initialize 'ResourceBundle' class
421 */
resourcebundle_register_class(void)422 void resourcebundle_register_class( void )
423 {
424 ResourceBundle_ce_ptr = register_class_ResourceBundle(zend_ce_aggregate, zend_ce_countable);
425 ResourceBundle_ce_ptr->create_object = ResourceBundle_object_create;
426 ResourceBundle_ce_ptr->default_object_handlers = &ResourceBundle_object_handlers;
427 ResourceBundle_ce_ptr->get_iterator = resourcebundle_get_iterator;
428
429 ResourceBundle_object_handlers = std_object_handlers;
430 ResourceBundle_object_handlers.offset = XtOffsetOf(ResourceBundle_object, zend);
431 ResourceBundle_object_handlers.clone_obj = NULL; /* ICU ResourceBundle has no clone implementation */
432 ResourceBundle_object_handlers.free_obj = ResourceBundle_object_free;
433 ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get;
434 ResourceBundle_object_handlers.count_elements = resourcebundle_array_count;
435 }
436 /* }}} */
437