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: Kirti Velankar <kirtig@yahoo-inc.com> |
12 | Gustavo Lopes <cataphract@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 #include "../intl_cppshims.h"
17
18 #include <unicode/timezone.h>
19 #include <unicode/calendar.h>
20 #include <unicode/datefmt.h>
21
22 extern "C" {
23 #include <unicode/ustring.h>
24 #include <unicode/udat.h>
25
26 #include "php_intl.h"
27 #include "dateformat_create.h"
28 #include "dateformat_class.h"
29 #define USE_CALENDAR_POINTER 1
30 #include "../calendar/calendar_class.h"
31 #define USE_TIMEZONE_POINTER 1
32 #include "../timezone/timezone_class.h"
33 #include "../intl_convert.h"
34 }
35
36 #include "dateformat_helpers.h"
37 #include "zend_exceptions.h"
38
39 #define INTL_UDATE_FMT_OK(i) \
40 (UDAT_FULL == (i) || UDAT_LONG == (i) || \
41 UDAT_MEDIUM == (i) || UDAT_SHORT == (i) || \
42 UDAT_RELATIVE == (i) || UDAT_FULL_RELATIVE == (i) || \
43 UDAT_LONG_RELATIVE == (i) || UDAT_MEDIUM_RELATIVE == (i) || \
44 UDAT_SHORT_RELATIVE == (i) || UDAT_NONE == (i) || \
45 UDAT_PATTERN == (i))
46
47 /* {{{ */
datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS,zend_error_handling * error_handling,bool * error_handling_replaced)48 static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_handling, bool *error_handling_replaced)
49 {
50 zval *object;
51 char *locale_str;
52 size_t locale_len = 0;
53 Locale locale;
54 zend_long date_type = UDAT_FULL;
55 zend_long time_type = UDAT_FULL;
56 zend_object *calendar_obj = NULL;
57 zend_long calendar_long = 0;
58 bool calendar_is_null = 1;
59 Calendar *cal = NULL;
60 zend_long calendar_type;
61 bool calendar_owned;
62 zval *timezone_zv = NULL;
63 TimeZone *timezone = NULL;
64 bool explicit_tz;
65 char* pattern_str = NULL;
66 size_t pattern_str_len = 0;
67 UChar* svalue = NULL; /* UTF-16 pattern_str */
68 int32_t slength = 0;
69 IntlDateFormatter_object* dfo;
70
71 intl_error_reset(NULL);
72 object = return_value;
73
74 ZEND_PARSE_PARAMETERS_START(1, 6)
75 Z_PARAM_STRING_OR_NULL(locale_str, locale_len)
76 Z_PARAM_OPTIONAL
77 Z_PARAM_LONG(date_type)
78 Z_PARAM_LONG(time_type)
79 Z_PARAM_ZVAL(timezone_zv)
80 Z_PARAM_OBJ_OF_CLASS_OR_LONG_OR_NULL(calendar_obj, Calendar_ce_ptr, calendar_long, calendar_is_null)
81 Z_PARAM_STRING_OR_NULL(pattern_str, pattern_str_len)
82 ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
83
84 if (error_handling != NULL) {
85 zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, error_handling);
86 *error_handling_replaced = 1;
87 }
88
89 DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
90
91 if (DATE_FORMAT_OBJECT(dfo) != NULL) {
92 intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: cannot call constructor twice", 0);
93 return FAILURE;
94 }
95
96 if (!INTL_UDATE_FMT_OK(date_type)) {
97 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: invalid date format style", 0);
98 return FAILURE;
99 }
100 if (!INTL_UDATE_FMT_OK(time_type)) {
101 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: invalid time format style", 0);
102 return FAILURE;
103 }
104 if (date_type == UDAT_PATTERN && time_type != UDAT_PATTERN) {
105 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: time format must be UDAT_PATTERN if date format is UDAT_PATTERN", 0);
106 return FAILURE;
107 }
108
109 INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len);
110 if (locale_len == 0) {
111 locale_str = (char *) intl_locale_get_default();
112 }
113 locale = Locale::createFromName(locale_str);
114 /* get*Name accessors being set does not preclude being bogus */
115 if (locale.isBogus() || strlen(locale.getISO3Language()) == 0) {
116 goto error;
117 }
118
119 /* process calendar */
120 if (datefmt_process_calendar_arg(calendar_obj, calendar_long, calendar_is_null, locale, "datefmt_create",
121 INTL_DATA_ERROR_P(dfo), cal, calendar_type, calendar_owned) == FAILURE
122 ) {
123 goto error;
124 }
125
126 /* process timezone */
127 explicit_tz = timezone_zv != NULL && Z_TYPE_P(timezone_zv) != IS_NULL;
128
129 if (explicit_tz || calendar_owned ) {
130 //we have an explicit time zone or a non-object calendar
131 timezone = timezone_process_timezone_argument(timezone_zv,
132 INTL_DATA_ERROR_P(dfo), "datefmt_create");
133 if (timezone == NULL) {
134 goto error;
135 }
136 }
137
138 /* Convert pattern (if specified) to UTF-16. */
139 if (pattern_str && pattern_str_len > 0) {
140 intl_convert_utf8_to_utf16(&svalue, &slength,
141 pattern_str, pattern_str_len, &INTL_DATA_ERROR_CODE(dfo));
142 if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
143 /* object construction -> only set global error */
144 intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: "
145 "error converting pattern to UTF-16", 0);
146 goto error;
147 }
148 }
149
150 DATE_FORMAT_OBJECT(dfo) = udat_open((UDateFormatStyle)time_type,
151 (UDateFormatStyle)date_type, locale_str, NULL, 0, svalue,
152 slength, &INTL_DATA_ERROR_CODE(dfo));
153
154 if (pattern_str && pattern_str_len > 0) {
155 udat_applyPattern(DATE_FORMAT_OBJECT(dfo), true, svalue, slength);
156 if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
157 intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: error applying pattern", 0);
158 goto error;
159 }
160 }
161
162 if (!U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
163 DateFormat *df = (DateFormat*)DATE_FORMAT_OBJECT(dfo);
164 if (calendar_owned) {
165 df->adoptCalendar(cal);
166 calendar_owned = false;
167 } else {
168 df->setCalendar(*cal);
169 }
170
171 if (timezone != NULL) {
172 df->adoptTimeZone(timezone);
173 }
174 } else {
175 intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: date "
176 "formatter creation failed", 0);
177 goto error;
178 }
179
180 /* Set the class variables */
181 dfo->date_type = date_type;
182 dfo->time_type = time_type;
183 dfo->calendar = calendar_type;
184 dfo->requested_locale = estrdup(locale_str);
185
186 error:
187 if (svalue) {
188 efree(svalue);
189 }
190 if (timezone != NULL && DATE_FORMAT_OBJECT(dfo) == NULL) {
191 delete timezone;
192 }
193 if (cal != NULL && calendar_owned) {
194 delete cal;
195 }
196
197 return U_FAILURE(intl_error_get_code(NULL)) ? FAILURE : SUCCESS;
198 }
199 /* }}} */
200
201 /* {{{ Create formatter. */
PHP_FUNCTION(datefmt_create)202 U_CFUNC PHP_FUNCTION( datefmt_create )
203 {
204 object_init_ex( return_value, IntlDateFormatter_ce_ptr );
205 if (datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, NULL) == FAILURE) {
206 zval_ptr_dtor(return_value);
207 RETURN_NULL();
208 }
209 }
210 /* }}} */
211
212 /* {{{ IntlDateFormatter object constructor. */
PHP_METHOD(IntlDateFormatter,__construct)213 U_CFUNC PHP_METHOD( IntlDateFormatter, __construct )
214 {
215 zend_error_handling error_handling;
216 bool error_handling_replaced = 0;
217
218 /* return_value param is being changed, therefore we will always return
219 * NULL here */
220 return_value = ZEND_THIS;
221 if (datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, &error_handling, &error_handling_replaced) == FAILURE) {
222 if (!EG(exception)) {
223 zend_string *err = intl_error_get_message(NULL);
224 zend_throw_exception(IntlException_ce_ptr, ZSTR_VAL(err), intl_error_get_code(NULL));
225 zend_string_release_ex(err, 0);
226 }
227 }
228 if (error_handling_replaced) {
229 zend_restore_error_handling(&error_handling);
230 }
231 }
232 /* }}} */
233