xref: /php-src/ext/intl/formatter/formatter_attr.c (revision 3fb71980)
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: Stanislav Malyshev <stas@zend.com>                          |
12    +----------------------------------------------------------------------+
13  */
14 
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 
19 #include "php_intl.h"
20 #include "formatter_class.h"
21 #include "intl_convert.h"
22 
23 #include <unicode/ustring.h>
24 
25 /* {{{ Get formatter attribute value. */
PHP_FUNCTION(numfmt_get_attribute)26 PHP_FUNCTION( numfmt_get_attribute )
27 {
28 	zend_long attribute, value;
29 	FORMATTER_METHOD_INIT_VARS;
30 
31 	/* Parse parameters. */
32 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ol",
33 		&object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
34 	{
35 		RETURN_THROWS();
36 	}
37 
38 	/* Fetch the object. */
39 	FORMATTER_METHOD_FETCH_OBJECT;
40 
41 	switch(attribute) {
42 		case UNUM_PARSE_INT_ONLY:
43 		case UNUM_GROUPING_USED:
44 		case UNUM_DECIMAL_ALWAYS_SHOWN:
45 		case UNUM_MAX_INTEGER_DIGITS:
46 		case UNUM_MIN_INTEGER_DIGITS:
47 		case UNUM_INTEGER_DIGITS:
48 		case UNUM_MAX_FRACTION_DIGITS:
49 		case UNUM_MIN_FRACTION_DIGITS:
50 		case UNUM_FRACTION_DIGITS:
51 		case UNUM_MULTIPLIER:
52 		case UNUM_GROUPING_SIZE:
53 		case UNUM_ROUNDING_MODE:
54 		case UNUM_FORMAT_WIDTH:
55 		case UNUM_PADDING_POSITION:
56 		case UNUM_SECONDARY_GROUPING_SIZE:
57 		case UNUM_SIGNIFICANT_DIGITS_USED:
58 		case UNUM_MIN_SIGNIFICANT_DIGITS:
59 		case UNUM_MAX_SIGNIFICANT_DIGITS:
60 		case UNUM_LENIENT_PARSE:
61 			value = unum_getAttribute(FORMATTER_OBJECT(nfo), attribute);
62 			if(value == -1) {
63 				INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
64 			} else {
65 				RETVAL_LONG(value);
66 			}
67 			break;
68 		case UNUM_ROUNDING_INCREMENT:
69 		{
70 			double value_double = unum_getDoubleAttribute(FORMATTER_OBJECT(nfo), attribute);
71 			if(value_double == -1) {
72 				INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
73 			} else {
74 				RETVAL_DOUBLE(value_double);
75 			}
76 		}
77 			break;
78 		default:
79 			INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
80 			break;
81 	}
82 
83 	INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
84 }
85 /* }}} */
86 
87 /* {{{ Get formatter attribute value. */
PHP_FUNCTION(numfmt_get_text_attribute)88 PHP_FUNCTION( numfmt_get_text_attribute )
89 {
90 	zend_long   attribute;
91 	UChar   value_buf[64];
92 	int32_t value_buf_size = USIZE( value_buf );
93 	UChar*  value  = value_buf;
94 	int32_t length = 0;
95 	FORMATTER_METHOD_INIT_VARS;
96 
97 	/* Parse parameters. */
98 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ol",
99 		&object, NumberFormatter_ce_ptr, &attribute ) == FAILURE )
100 	{
101 		RETURN_THROWS();
102 	}
103 
104 	/* Fetch the object. */
105 	FORMATTER_METHOD_FETCH_OBJECT;
106 
107 	length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) );
108 	if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= value_buf_size) {
109 		++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
110 		INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
111 		value = eumalloc(length);
112 		length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) );
113 		if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
114 			efree(value);
115 			value = value_buf;
116 		}
117 	}
118 	INTL_METHOD_CHECK_STATUS( nfo, "Error getting attribute value" );
119 
120 	INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
121 }
122 /* }}} */
123 
124 /* {{{ Get formatter attribute value. */
PHP_FUNCTION(numfmt_set_attribute)125 PHP_FUNCTION( numfmt_set_attribute )
126 {
127 	zend_long attribute;
128 	zval *value;
129 	FORMATTER_METHOD_INIT_VARS;
130 
131 	/* Parse parameters. */
132 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oln",
133 		&object, NumberFormatter_ce_ptr, &attribute, &value ) == FAILURE)
134 	{
135 		RETURN_THROWS();
136 	}
137 
138 	/* Fetch the object. */
139 	FORMATTER_METHOD_FETCH_OBJECT;
140 
141 	switch(attribute) {
142 		case UNUM_PARSE_INT_ONLY:
143 		case UNUM_GROUPING_USED:
144 		case UNUM_DECIMAL_ALWAYS_SHOWN:
145 		case UNUM_MAX_INTEGER_DIGITS:
146 		case UNUM_MIN_INTEGER_DIGITS:
147 		case UNUM_INTEGER_DIGITS:
148 		case UNUM_MAX_FRACTION_DIGITS:
149 		case UNUM_MIN_FRACTION_DIGITS:
150 		case UNUM_FRACTION_DIGITS:
151 		case UNUM_MULTIPLIER:
152 		case UNUM_GROUPING_SIZE:
153 		case UNUM_ROUNDING_MODE:
154 		case UNUM_FORMAT_WIDTH:
155 		case UNUM_PADDING_POSITION:
156 		case UNUM_SECONDARY_GROUPING_SIZE:
157 		case UNUM_SIGNIFICANT_DIGITS_USED:
158 		case UNUM_MIN_SIGNIFICANT_DIGITS:
159 		case UNUM_MAX_SIGNIFICANT_DIGITS:
160 		case UNUM_LENIENT_PARSE:
161 			unum_setAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_long(value));
162 			break;
163 		case UNUM_ROUNDING_INCREMENT:
164 			unum_setDoubleAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_double(value));
165 			break;
166 		default:
167 			INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR;
168 			break;
169 	}
170 
171 	INTL_METHOD_CHECK_STATUS( nfo, "Error setting attribute value" );
172 
173 	RETURN_TRUE;
174 }
175 /* }}} */
176 
177 /* {{{ Get formatter attribute value. */
PHP_FUNCTION(numfmt_set_text_attribute)178 PHP_FUNCTION( numfmt_set_text_attribute )
179 {
180 	int32_t slength = 0;
181 	UChar *svalue = NULL;
182 	zend_long attribute;
183 	char *value;
184 	size_t len;
185 	FORMATTER_METHOD_INIT_VARS;
186 
187 	/* Parse parameters. */
188 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ols",
189 		&object, NumberFormatter_ce_ptr, &attribute, &value, &len ) == FAILURE)
190 	{
191 		RETURN_THROWS();
192 	}
193 
194 	/* Fetch the object. */
195 	FORMATTER_METHOD_FETCH_OBJECT;
196 
197 	/* Convert given attribute value to UTF-16. */
198 	intl_convert_utf8_to_utf16(&svalue, &slength, value, len, &INTL_DATA_ERROR_CODE(nfo));
199 	INTL_METHOD_CHECK_STATUS( nfo, "Error converting attribute value to UTF-16" );
200 
201 	/* Actually set new attribute value. */
202 	unum_setTextAttribute(FORMATTER_OBJECT(nfo), attribute, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
203 	if (svalue) {
204 		efree(svalue);
205 	}
206 	INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" );
207 
208 	RETURN_TRUE;
209 }
210 /* }}} */
211 
212 /* {{{ Get formatter symbol value. */
PHP_FUNCTION(numfmt_get_symbol)213 PHP_FUNCTION( numfmt_get_symbol )
214 {
215 	zend_long symbol;
216 	UChar value_buf[4];
217 	UChar *value = value_buf;
218 	uint32_t length = USIZE(value_buf);
219 	FORMATTER_METHOD_INIT_VARS;
220 
221 	/* Parse parameters. */
222 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ol",
223 		&object, NumberFormatter_ce_ptr, &symbol ) == FAILURE )
224 	{
225 		RETURN_THROWS();
226 	}
227 
228 	if(symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) {
229 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,	"numfmt_get_symbol: invalid symbol value", 0 );
230 		RETURN_FALSE;
231 	}
232 
233 	/* Fetch the object. */
234 	FORMATTER_METHOD_FETCH_OBJECT;
235 
236 	length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo));
237 	if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
238 		++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
239 		INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
240 		value = eumalloc(length);
241 		length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo));
242 		if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
243 			efree(value);
244 			value = value_buf;
245 		}
246 	}
247 	INTL_METHOD_CHECK_STATUS( nfo, "Error getting symbol value" );
248 
249 	INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value_buf != value ) );
250 }
251 /* }}} */
252 
253 /* {{{ Set formatter symbol value. */
PHP_FUNCTION(numfmt_set_symbol)254 PHP_FUNCTION( numfmt_set_symbol )
255 {
256 	zend_long  symbol;
257 	char*      value     = NULL;
258 	size_t     value_len = 0;
259 	UChar*     svalue  = 0;
260 	int32_t    slength = 0;
261 	FORMATTER_METHOD_INIT_VARS;
262 
263 	/* Parse parameters. */
264 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ols",
265 		&object, NumberFormatter_ce_ptr, &symbol, &value, &value_len ) == FAILURE )
266 	{
267 		RETURN_THROWS();
268 	}
269 
270 	if (symbol >= UNUM_FORMAT_SYMBOL_COUNT || symbol < 0) {
271 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,	"numfmt_set_symbol: invalid symbol value", 0 );
272 		RETURN_FALSE;
273 	}
274 
275 	/* Fetch the object. */
276 	FORMATTER_METHOD_FETCH_OBJECT;
277 
278 	/* Convert given symbol to UTF-16. */
279 	intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
280 	INTL_METHOD_CHECK_STATUS( nfo, "Error converting symbol value to UTF-16" );
281 
282 	/* Actually set the symbol. */
283 	unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo));
284 	if (svalue) {
285 		efree(svalue);
286 	}
287 	INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" );
288 
289 	RETURN_TRUE;
290 }
291 /* }}} */
292 
293 /* {{{ Get formatter pattern. */
PHP_FUNCTION(numfmt_get_pattern)294 PHP_FUNCTION( numfmt_get_pattern )
295 {
296 	UChar   value_buf[64];
297 	uint32_t length = USIZE( value_buf );
298 	UChar*  value  = value_buf;
299 	FORMATTER_METHOD_INIT_VARS;
300 
301 	/* Parse parameters. */
302 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O",
303 		&object, NumberFormatter_ce_ptr ) == FAILURE )
304 	{
305 		RETURN_THROWS();
306 	}
307 
308 	/* Fetch the object. */
309 	FORMATTER_METHOD_FETCH_OBJECT;
310 
311 	length = unum_toPattern(FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo));
312 	if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) {
313 		++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */
314 		INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR;
315 		value = eumalloc(length);
316 		length = unum_toPattern( FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) );
317 		if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
318 			efree(value);
319 			value = value_buf;
320 		}
321 	}
322 	INTL_METHOD_CHECK_STATUS( nfo, "Error getting formatter pattern" );
323 
324 	INTL_METHOD_RETVAL_UTF8( nfo, value, length, ( value != value_buf ) );
325 }
326 /* }}} */
327 
328 /* {{{ Set formatter pattern. */
PHP_FUNCTION(numfmt_set_pattern)329 PHP_FUNCTION( numfmt_set_pattern )
330 {
331 	char*       value = NULL;
332 	size_t      value_len = 0;
333 	int32_t     slength = 0;
334 	UChar*	    svalue  = NULL;
335 	UParseError spattern_error = {0};
336 	FORMATTER_METHOD_INIT_VARS;
337 
338 	/* Parse parameters. */
339 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Os",
340 		&object, NumberFormatter_ce_ptr, &value, &value_len ) == FAILURE )
341 	{
342 		RETURN_THROWS();
343 	}
344 
345 	FORMATTER_METHOD_FETCH_OBJECT;
346 
347 	/* Convert given pattern to UTF-16. */
348 	intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo));
349 	INTL_METHOD_CHECK_STATUS( nfo, "Error converting pattern to UTF-16" );
350 
351 	unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, &spattern_error, &INTL_DATA_ERROR_CODE(nfo));
352 	if (svalue) {
353 		efree(svalue);
354 	}
355 	if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) {
356 		char *msg;
357 		spprintf(&msg, 0, "Error setting pattern value at line %d, offset %d", spattern_error.line, spattern_error.offset);
358 		intl_errors_set_custom_msg(INTL_DATA_ERROR_P(nfo), msg, 1);
359 		efree(msg);
360 		RETURN_FALSE;
361 	}
362 
363 	RETURN_TRUE;
364 }
365 /* }}} */
366 
367 /* {{{ Get formatter locale. */
PHP_FUNCTION(numfmt_get_locale)368 PHP_FUNCTION( numfmt_get_locale )
369 {
370 	zend_long type = ULOC_ACTUAL_LOCALE;
371 	char* loc;
372 	FORMATTER_METHOD_INIT_VARS;
373 
374 	/* Parse parameters. */
375 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "O|l",
376 		&object, NumberFormatter_ce_ptr, &type ) == FAILURE )
377 	{
378 		RETURN_THROWS();
379 	}
380 
381 	/* Fetch the object. */
382 	FORMATTER_METHOD_FETCH_OBJECT;
383 
384 	loc = (char *)unum_getLocaleByType(FORMATTER_OBJECT(nfo), type, &INTL_DATA_ERROR_CODE(nfo));
385 	INTL_METHOD_CHECK_STATUS( nfo, "Error getting locale" );
386 	RETURN_STRING(loc);
387 }
388 /* }}} */
389