1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
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_PP(z) == IS_LONG) {
50 long lval = Z_LVAL_PP(z);
51 for (int i = 0; i < sizeof(valid_styles) / sizeof(*valid_styles); i++) {
52 if ((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 int 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() TSRMLS_CC, "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(TSRMLS_C);
83 }
84
85 if (format == NULL || Z_TYPE_PP(format) == IS_NULL) {
86 //nothing
87 } else if (Z_TYPE_PP(format) == IS_ARRAY) {
88 HashTable *ht = Z_ARRVAL_PP(format);
89 HashPosition pos = {0};
90 zval **z;
91 if (zend_hash_num_elements(ht) != 2) {
92 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
93 "datefmt_format_object: bad format; if array, it must have "
94 "two elements", 0 TSRMLS_CC);
95 RETURN_FALSE;
96 }
97
98 zend_hash_internal_pointer_reset_ex(ht, &pos);
99 zend_hash_get_current_data_ex(ht, (void**)&z, &pos);
100 if (!valid_format(z)) {
101 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
102 "datefmt_format_object: bad format; the date format (first "
103 "element of the array) is not valid", 0 TSRMLS_CC);
104 RETURN_FALSE;
105 }
106 dateStyle = (DateFormat::EStyle)Z_LVAL_PP(z);
107
108 zend_hash_move_forward_ex(ht, &pos);
109 zend_hash_get_current_data_ex(ht, (void**)&z, &pos);
110 if (!valid_format(z)) {
111 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
112 "datefmt_format_object: bad format; the time format ("
113 "second element of the array) is not valid", 0 TSRMLS_CC);
114 RETURN_FALSE;
115 }
116 timeStyle = (DateFormat::EStyle)Z_LVAL_PP(z);
117 } else if (Z_TYPE_PP(format) == IS_LONG) {
118 if (!valid_format(format)) {
119 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
120 "datefmt_format_object: the date/time format type is invalid",
121 0 TSRMLS_CC);
122 RETURN_FALSE;
123 }
124 dateStyle = timeStyle = (DateFormat::EStyle)Z_LVAL_PP(format);
125 } else {
126 convert_to_string_ex(format);
127 if (Z_STRLEN_PP(format) == 0) {
128 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
129 "datefmt_format_object: the format is empty", 0 TSRMLS_CC);
130 RETURN_FALSE;
131 }
132 pattern = true;
133 }
134
135 //there's no support for relative time in ICU yet
136 timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative);
137
138 zend_class_entry *instance_ce = Z_OBJCE_P(object);
139 if (instanceof_function(instance_ce, Calendar_ce_ptr TSRMLS_CC)) {
140 Calendar *obj_cal = calendar_fetch_native_calendar(object TSRMLS_CC);
141 if (obj_cal == NULL) {
142 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
143 "datefmt_format_object: bad IntlCalendar instance: "
144 "not initialized properly", 0 TSRMLS_CC);
145 RETURN_FALSE;
146 }
147 timeZone = obj_cal->getTimeZone().clone();
148 date = obj_cal->getTime(status);
149 if (U_FAILURE(status)) {
150 intl_error_set(NULL, status,
151 "datefmt_format_object: error obtaining instant from "
152 "IntlCalendar", 0 TSRMLS_CC);
153 RETVAL_FALSE;
154 goto cleanup;
155 }
156 cal = obj_cal->clone();
157 } else if (instanceof_function(instance_ce, php_date_get_date_ce() TSRMLS_CC)) {
158 if (intl_datetime_decompose(object, &date, &timeZone, NULL,
159 "datefmt_format_object" TSRMLS_CC) == FAILURE) {
160 RETURN_FALSE;
161 }
162 cal = new GregorianCalendar(Locale::createFromName(locale_str), status);
163 if (U_FAILURE(status)) {
164 intl_error_set(NULL, status,
165 "datefmt_format_object: could not create GregorianCalendar",
166 0 TSRMLS_CC);
167 RETVAL_FALSE;
168 goto cleanup;
169 }
170 } else {
171 intl_error_set(NULL, status, "datefmt_format_object: the passed object "
172 "must be an instance of either IntlCalendar or DateTime",
173 0 TSRMLS_CC);
174 RETURN_FALSE;
175 }
176
177 if (pattern) {
178 df = new SimpleDateFormat(
179 UnicodeString(Z_STRVAL_PP(format), Z_STRLEN_PP(format),
180 UnicodeString::kInvariant),
181 Locale::createFromName(locale_str),
182 status);
183
184 if (U_FAILURE(status)) {
185 intl_error_set(NULL, status,
186 "datefmt_format_object: could not create SimpleDateFormat",
187 0 TSRMLS_CC);
188 RETVAL_FALSE;
189 goto cleanup;
190 }
191 } else {
192 df = DateFormat::createDateTimeInstance(dateStyle, timeStyle,
193 Locale::createFromName(locale_str));
194
195 if (df == NULL) { /* according to ICU sources, this should never happen */
196 intl_error_set(NULL, status,
197 "datefmt_format_object: could not create DateFormat",
198 0 TSRMLS_CC);
199 RETVAL_FALSE;
200 goto cleanup;
201 }
202 }
203
204 //must be in this order (or have the cal adopt the tz)
205 df->adoptCalendar(cal);
206 cal = NULL;
207 df->adoptTimeZone(timeZone);
208 timeZone = NULL;
209
210 {
211 UnicodeString result = UnicodeString();
212 df->format(date, result);
213
214 Z_TYPE_P(return_value) = IS_STRING;
215 if (intl_charFromString(result, &Z_STRVAL_P(return_value),
216 &Z_STRLEN_P(return_value), &status) == FAILURE) {
217 intl_error_set(NULL, status,
218 "datefmt_format_object: error converting result to UTF-8",
219 0 TSRMLS_CC);
220 RETVAL_FALSE;
221 goto cleanup;
222 }
223 }
224
225
226 cleanup:
227 delete df;
228 delete timeZone;
229 delete cal;
230 }
231