1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | http://www.php.net/license/3_01.txt                                  |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Stanislav Malyshev <stas@zend.com>                          |
14    +----------------------------------------------------------------------+
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include <unicode/ustring.h>
22 
23 #include "php_intl.h"
24 #include "formatter_class.h"
25 #include "formatter_format.h"
26 #include "intl_convert.h"
27 
28 /* {{{ proto mixed NumberFormatter::format( mixed $num[, int $type] )
29  * Format a number. }}} */
30 /* {{{ proto mixed numfmt_format( NumberFormatter $nf, mixed $num[, int type] )
31  * Format a number.
32  */
PHP_FUNCTION(numfmt_format)33 PHP_FUNCTION( numfmt_format )
34 {
35 	zval *number;
36 	zend_long type = FORMAT_TYPE_DEFAULT;
37 	UChar format_buf[32];
38 	UChar* formatted = format_buf;
39 	int32_t formatted_len = USIZE(format_buf);
40 	FORMATTER_METHOD_INIT_VARS;
41 
42 	/* Parse parameters. */
43 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Oz|l",
44 		&object, NumberFormatter_ce_ptr,  &number, &type ) == FAILURE )
45 	{
46 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
47 			"numfmt_format: unable to parse input params", 0 );
48 
49 		RETURN_FALSE;
50 	}
51 
52 	/* Fetch the object. */
53 	FORMATTER_METHOD_FETCH_OBJECT;
54 
55 	if(type == FORMAT_TYPE_DEFAULT) {
56 		if(Z_TYPE_P(number) == IS_STRING) {
57 			convert_scalar_to_number_ex(number);
58 		}
59 
60 		if(Z_TYPE_P(number) == IS_LONG) {
61 			/* take INT32 on 32-bit, int64 on 64-bit */
62 			type = (sizeof(zend_long) == 8)?FORMAT_TYPE_INT64:FORMAT_TYPE_INT32;
63 		} else if(Z_TYPE_P(number) == IS_DOUBLE) {
64 			type = FORMAT_TYPE_DOUBLE;
65 		} else {
66 			type = FORMAT_TYPE_INT32;
67 		}
68 	}
69 
70 	if(Z_TYPE_P(number) != IS_DOUBLE && Z_TYPE_P(number) != IS_LONG) {
71 		convert_scalar_to_number(number );
72 	}
73 
74 	switch(type) {
75 		case FORMAT_TYPE_INT32:
76 			convert_to_long_ex(number);
77 			formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
78 				formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
79 			if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
80 				intl_error_reset(INTL_DATA_ERROR_P(nfo));
81 				formatted = eumalloc(formatted_len);
82 				formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number),
83 					formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
84 				if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
85 					efree(formatted);
86 				}
87 			}
88 			INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
89 			break;
90 
91 		case FORMAT_TYPE_INT64:
92 		{
93 			int64_t value = (Z_TYPE_P(number) == IS_DOUBLE)?(int64_t)Z_DVAL_P(number):Z_LVAL_P(number);
94 			formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
95 			if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
96 				intl_error_reset(INTL_DATA_ERROR_P(nfo));
97 				formatted = eumalloc(formatted_len);
98 				formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
99 				if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
100 					efree(formatted);
101 				}
102 			}
103 			INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
104 		}
105 			break;
106 
107 		case FORMAT_TYPE_DOUBLE:
108 			convert_to_double_ex(number);
109 			formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
110 			if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
111 				intl_error_reset(INTL_DATA_ERROR_P(nfo));
112 				formatted = eumalloc(formatted_len);
113 				unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
114 				if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) {
115 					efree(formatted);
116 				}
117 			}
118 			INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" );
119 			break;
120 
121 		default:
122 			php_error_docref(NULL, E_WARNING, "Unsupported format type %pd", type);
123 			RETURN_FALSE;
124 			break;
125 	}
126 
127 	INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
128 }
129 /* }}} */
130 
131 /* {{{ proto mixed NumberFormatter::formatCurrency( double $num, string $currency )
132  * Format a number as currency. }}} */
133 /* {{{ proto mixed numfmt_format_currency( NumberFormatter $nf, double $num, string $currency )
134  * Format a number as currency.
135  */
PHP_FUNCTION(numfmt_format_currency)136 PHP_FUNCTION( numfmt_format_currency )
137 {
138 	double     number;
139 	UChar      format_buf[32];
140 	UChar*     formatted     = format_buf;
141 	int32_t    formatted_len = USIZE(format_buf);
142 	char*      currency      = NULL;
143 	size_t     currency_len  = 0;
144 	UChar*     scurrency     = NULL;
145 	int32_t    scurrency_len = 0;
146 	FORMATTER_METHOD_INIT_VARS;
147 
148 	/* Parse parameters. */
149 	if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Ods",
150 		&object, NumberFormatter_ce_ptr,  &number, &currency, &currency_len ) == FAILURE )
151 	{
152 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
153 			"numfmt_format_currency: unable to parse input params", 0 );
154 
155 		RETURN_FALSE;
156 	}
157 
158 	/* Fetch the object. */
159 	FORMATTER_METHOD_FETCH_OBJECT;
160 
161 	/* Convert currency to UTF-16. */
162 	intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo));
163 	INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-16 failed" );
164 
165 	/* Format the number using a fixed-length buffer. */
166 	formatted_len = unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
167 
168 	/* If the buffer turned out to be too small
169 	 * then allocate another buffer dynamically
170 	 * and use it to format the number.
171 	 */
172 	if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) {
173 		intl_error_reset(INTL_DATA_ERROR_P(nfo));
174 		formatted = eumalloc(formatted_len);
175 		unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo));
176 	}
177 
178 	if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) {
179 		intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((nfo)) );
180 		intl_errors_set_custom_msg( INTL_DATA_ERROR_P(nfo), "Number formatting failed", 0 );
181 		RETVAL_FALSE;
182 		if (formatted != format_buf) {
183 			efree(formatted);
184 		}
185 	} else {
186 		INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) );
187 	}
188 
189 	if(scurrency) {
190 		efree(scurrency);
191 	}
192 }
193 
194 /* }}} */
195 
196 /*
197  * Local variables:
198  * tab-width: 4
199  * c-basic-offset: 4
200  * End:
201  * vim600: noet sw=4 ts=4 fdm=marker
202  * vim<600: noet sw=4 ts=4
203  */
204