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: Gustavo Lopes <cataphract@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "../intl_cppshims.h"
18
19 #include <unicode/calendar.h>
20 #include <unicode/gregocal.h>
21 #include <unicode/datefmt.h>
22 #include <unicode/smpdtfmt.h>
23 #include <unicode/locid.h>
24
25 #include "../intl_convertcpp.h"
26
27 extern "C" {
28 #include "../php_intl.h"
29 #include "../locale/locale.h"
30 #define USE_CALENDAR_POINTER 1
31 #include "../calendar/calendar_class.h"
32 #include <ext/date/php_date.h>
33 #include "../common/common_date.h"
34 }
35
36 static const DateFormat::EStyle valid_styles[] = {
37 DateFormat::kNone,
38 DateFormat::kFull,
39 DateFormat::kLong,
40 DateFormat::kMedium,
41 DateFormat::kShort,
42 DateFormat::kFullRelative,
43 DateFormat::kLongRelative,
44 DateFormat::kMediumRelative,
45 DateFormat::kShortRelative,
46 };
47
valid_format(zval * z)48 static bool valid_format(zval *z) {
49 if (Z_TYPE_P(z) == IS_LONG) {
50 zend_long lval = Z_LVAL_P(z);
51 for (int i = 0; i < sizeof(valid_styles) / sizeof(*valid_styles); i++) {
52 if ((zend_long)valid_styles[i] == lval) {
53 return true;
54 }
55 }
56 }
57
58 return false;
59 }
60
PHP_FUNCTION(datefmt_format_object)61 U_CFUNC PHP_FUNCTION(datefmt_format_object)
62 {
63 zval *object,
64 *format = NULL;
65 const char *locale_str = NULL;
66 size_t locale_len;
67 bool pattern = false;
68 UDate date;
69 TimeZone *timeZone = NULL;
70 UErrorCode status = U_ZERO_ERROR;
71 DateFormat *df = NULL;
72 Calendar *cal = NULL;
73 DateFormat::EStyle dateStyle = DateFormat::kDefault,
74 timeStyle = DateFormat::kDefault;
75
76 if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|zs!",
77 &object, &format, &locale_str, &locale_len) == FAILURE) {
78 RETURN_FALSE;
79 }
80
81 if (!locale_str) {
82 locale_str = intl_locale_get_default();
83 }
84
85 if (format == NULL || Z_TYPE_P(format) == IS_NULL) {
86 //nothing
87 } else if (Z_TYPE_P(format) == IS_ARRAY) {
88 HashTable *ht = Z_ARRVAL_P(format);
89 uint32_t idx;
90 zval *z;
91
92 if (zend_hash_num_elements(ht) != 2) {
93 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
94 "datefmt_format_object: bad format; if array, it must have "
95 "two elements", 0);
96 RETURN_FALSE;
97 }
98
99 idx = 0;
100 while (idx < ht->nNumUsed) {
101 z = &ht->arData[idx].val;
102 if (Z_TYPE_P(z) != IS_UNDEF) {
103 break;
104 }
105 idx++;
106 }
107 if (idx >= ht->nNumUsed || !valid_format(z)) {
108 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
109 "datefmt_format_object: bad format; the date format (first "
110 "element of the array) is not valid", 0);
111 RETURN_FALSE;
112 }
113 dateStyle = (DateFormat::EStyle)Z_LVAL_P(z);
114
115 idx++;
116 while (idx < ht->nNumUsed) {
117 z = &ht->arData[idx].val;
118 if (Z_TYPE_P(z) != IS_UNDEF) {
119 break;
120 }
121 idx++;
122 }
123 if (idx >= ht->nNumUsed || !valid_format(z)) {
124 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
125 "datefmt_format_object: bad format; the time format ("
126 "second element of the array) is not valid", 0);
127 RETURN_FALSE;
128 }
129 timeStyle = (DateFormat::EStyle)Z_LVAL_P(z);
130 } else if (Z_TYPE_P(format) == IS_LONG) {
131 if (!valid_format(format)) {
132 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
133 "datefmt_format_object: the date/time format type is invalid",
134 0);
135 RETURN_FALSE;
136 }
137 dateStyle = timeStyle = (DateFormat::EStyle)Z_LVAL_P(format);
138 } else {
139 convert_to_string_ex(format);
140 if (Z_STRLEN_P(format) == 0) {
141 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
142 "datefmt_format_object: the format is empty", 0);
143 RETURN_FALSE;
144 }
145 pattern = true;
146 }
147
148 //there's no support for relative time in ICU yet
149 timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
150
151 zend_class_entry *instance_ce = Z_OBJCE_P(object);
152 if (instanceof_function(instance_ce, Calendar_ce_ptr)) {
153 Calendar *obj_cal = calendar_fetch_native_calendar(object);
154 if (obj_cal == NULL) {
155 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
156 "datefmt_format_object: bad IntlCalendar instance: "
157 "not initialized properly", 0);
158 RETURN_FALSE;
159 }
160 timeZone = obj_cal->getTimeZone().clone();
161 date = obj_cal->getTime(status);
162 if (U_FAILURE(status)) {
163 intl_error_set(NULL, status,
164 "datefmt_format_object: error obtaining instant from "
165 "IntlCalendar", 0);
166 RETVAL_FALSE;
167 goto cleanup;
168 }
169 cal = obj_cal->clone();
170 } else if (instanceof_function(instance_ce, php_date_get_date_ce())) {
171 if (intl_datetime_decompose(object, &date, &timeZone, NULL,
172 "datefmt_format_object") == FAILURE) {
173 RETURN_FALSE;
174 }
175 cal = new GregorianCalendar(Locale::createFromName(locale_str), status);
176 if (U_FAILURE(status)) {
177 intl_error_set(NULL, status,
178 "datefmt_format_object: could not create GregorianCalendar",
179 0);
180 RETVAL_FALSE;
181 goto cleanup;
182 }
183 } else {
184 intl_error_set(NULL, status, "datefmt_format_object: the passed object "
185 "must be an instance of either IntlCalendar or DateTime",
186 0);
187 RETURN_FALSE;
188 }
189
190 if (pattern) {
191 df = new SimpleDateFormat(
192 UnicodeString(Z_STRVAL_P(format), Z_STRLEN_P(format),
193 UnicodeString::kInvariant),
194 Locale::createFromName(locale_str),
195 status);
196
197 if (U_FAILURE(status)) {
198 intl_error_set(NULL, status,
199 "datefmt_format_object: could not create SimpleDateFormat",
200 0);
201 RETVAL_FALSE;
202 goto cleanup;
203 }
204 } else {
205 df = DateFormat::createDateTimeInstance(dateStyle, timeStyle,
206 Locale::createFromName(locale_str));
207
208 if (df == NULL) { /* according to ICU sources, this should never happen */
209 intl_error_set(NULL, status,
210 "datefmt_format_object: could not create DateFormat",
211 0);
212 RETVAL_FALSE;
213 goto cleanup;
214 }
215 }
216
217 //must be in this order (or have the cal adopt the tz)
218 df->adoptCalendar(cal);
219 cal = NULL;
220 df->adoptTimeZone(timeZone);
221 timeZone = NULL;
222
223 {
224 zend_string *u8str;
225 UnicodeString result = UnicodeString();
226 df->format(date, result);
227
228 u8str = intl_charFromString(result, &status);
229 if (!u8str) {
230 intl_error_set(NULL, status,
231 "datefmt_format_object: error converting result to UTF-8",
232 0);
233 RETVAL_FALSE;
234 goto cleanup;
235 }
236 RETVAL_STR(u8str);
237 }
238
239
240 cleanup:
241 delete df;
242 delete timeZone;
243 delete cal;
244 }
245