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
21 #include <unicode/ustring.h>
22 #include <locale.h>
23
24 #include "formatter_class.h"
25 #include "formatter_format.h"
26 #include "intl_convert.h"
27
28 #define ICU_LOCALE_BUG 1
29
30 /* {{{ Parse a number. */
PHP_FUNCTION(numfmt_parse)31 PHP_FUNCTION( numfmt_parse )
32 {
33 zend_long type = FORMAT_TYPE_DOUBLE;
34 UChar* sstr = NULL;
35 int32_t sstr_len = 0;
36 char* str = NULL;
37 size_t str_len;
38 int32_t val32, position = 0;
39 int64_t val64;
40 double val_double;
41 int32_t* position_p = NULL;
42 zval *zposition = NULL;
43 char *oldlocale;
44 FORMATTER_METHOD_INIT_VARS;
45
46 /* Parse parameters. */
47 if (zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Os|lz!",
48 &object, NumberFormatter_ce_ptr, &str, &str_len, &type, &zposition ) == FAILURE )
49 {
50 RETURN_THROWS();
51 }
52
53 if (zposition) {
54 position = (int32_t) zval_get_long(zposition);
55 position_p = &position;
56 }
57
58 /* Fetch the object. */
59 FORMATTER_METHOD_FETCH_OBJECT;
60
61 /* Convert given string to UTF-16. */
62 intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
63 INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
64
65 #if ICU_LOCALE_BUG && defined(LC_NUMERIC)
66 /* need to copy here since setlocale may change it later */
67 oldlocale = estrdup(setlocale(LC_NUMERIC, NULL));
68 setlocale(LC_NUMERIC, "C");
69 #endif
70
71 switch(type) {
72 case FORMAT_TYPE_INT32:
73 val32 = unum_parse(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
74 RETVAL_LONG(val32);
75 break;
76 case FORMAT_TYPE_INT64:
77 val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
78 if(val64 > ZEND_LONG_MAX || val64 < ZEND_LONG_MIN) {
79 RETVAL_DOUBLE(val64);
80 } else {
81 RETVAL_LONG((zend_long)val64);
82 }
83 break;
84 case FORMAT_TYPE_DOUBLE:
85 val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo));
86 RETVAL_DOUBLE(val_double);
87 break;
88 case FORMAT_TYPE_CURRENCY:
89 if (getThis()) {
90 const char *space;
91 const char *class_name = get_active_class_name(&space);
92 zend_argument_value_error(2, "cannot be NumberFormatter::TYPE_CURRENCY constant, "
93 "use %s%sparseCurrency() method instead", class_name, space);
94 } else {
95 zend_argument_value_error(3, "cannot be NumberFormatter::TYPE_CURRENCY constant, use numfmt_parse_currency() function instead");
96 }
97 goto cleanup;
98 default:
99 zend_argument_value_error(hasThis() ? 2 : 3, "must be a NumberFormatter::TYPE_* constant");
100 goto cleanup;
101 }
102
103 if (zposition) {
104 ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
105 }
106
107 cleanup:
108
109 #if ICU_LOCALE_BUG && defined(LC_NUMERIC)
110 setlocale(LC_NUMERIC, oldlocale);
111 efree(oldlocale);
112 #endif
113
114 if (sstr) {
115 efree(sstr);
116 }
117
118 INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
119 }
120 /* }}} */
121
122 /* {{{ Parse a number as currency. */
PHP_FUNCTION(numfmt_parse_currency)123 PHP_FUNCTION( numfmt_parse_currency )
124 {
125 double number;
126 UChar currency[5] = {0};
127 UChar* sstr = NULL;
128 int32_t sstr_len = 0;
129 zend_string *u8str;
130 char *str;
131 size_t str_len;
132 int32_t* position_p = NULL;
133 int32_t position = 0;
134 zval *zcurrency, *zposition = NULL;
135 FORMATTER_METHOD_INIT_VARS;
136
137 /* Parse parameters. */
138 if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "Osz/|z!",
139 &object, NumberFormatter_ce_ptr, &str, &str_len, &zcurrency, &zposition ) == FAILURE )
140 {
141 RETURN_THROWS();
142 }
143
144 /* Fetch the object. */
145 FORMATTER_METHOD_FETCH_OBJECT;
146
147 /* Convert given string to UTF-16. */
148 intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo));
149 INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" );
150
151 if(zposition) {
152 position = (int32_t) zval_get_long(zposition);
153 position_p = &position;
154 }
155
156 number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo));
157 if(zposition) {
158 ZEND_TRY_ASSIGN_REF_LONG(zposition, position);
159 }
160 if (sstr) {
161 efree(sstr);
162 }
163 INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" );
164
165 /* Convert parsed currency to UTF-8 and pass it back to caller. */
166 u8str = intl_convert_utf16_to_utf8(currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo));
167 INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" );
168 zval_ptr_dtor( zcurrency );
169 ZVAL_NEW_STR(zcurrency, u8str);
170
171 RETVAL_DOUBLE( number );
172 }
173 /* }}} */
174