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 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "../intl_cppshims.h"
22
23 #include <unicode/locid.h>
24 #include <unicode/calendar.h>
25 #include <unicode/gregocal.h>
26 #include <unicode/ustring.h>
27
28 extern "C" {
29 #include "../php_intl.h"
30 #include "../intl_common.h"
31 #define USE_TIMEZONE_POINTER 1
32 #include "../timezone/timezone_class.h"
33 #define USE_CALENDAR_POINTER 1
34 #include "calendar_class.h"
35 #include <ext/date/php_date.h>
36 #include "zend_exceptions.h"
37 }
38
39 using icu::GregorianCalendar;
40 using icu::Locale;
41 using icu::UnicodeString;
42 using icu::StringPiece;
43
fetch_greg(Calendar_object * co)44 static inline GregorianCalendar *fetch_greg(Calendar_object *co) {
45 return (GregorianCalendar*)co->ucal;
46 }
47
_php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS,zend_bool is_constructor)48 static void _php_intlgregcal_constructor_body(
49 INTERNAL_FUNCTION_PARAMETERS, zend_bool is_constructor)
50 {
51 zval *tz_object = NULL;
52 zval args_a[6] = {0},
53 *args = &args_a[0];
54 char *locale = NULL;
55 size_t locale_len;
56 zend_long largs[6];
57 UErrorCode status = U_ZERO_ERROR;
58 int variant;
59 int zpp_flags = is_constructor ? ZEND_PARSE_PARAMS_THROW : 0;
60 intl_error_reset(NULL);
61
62 // parameter number validation / variant determination
63 if (ZEND_NUM_ARGS() > 6 ||
64 zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
65 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
66 "intlgregcal_create_instance: too many arguments", 0);
67 if (!is_constructor) {
68 zval_ptr_dtor(return_value);
69 RETVAL_NULL();
70 }
71 return;
72 }
73 for (variant = ZEND_NUM_ARGS();
74 variant > 0 && Z_TYPE(args[variant - 1]) == IS_NULL;
75 variant--) {}
76 if (variant == 4) {
77 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
78 "intlgregcal_create_instance: no variant with 4 arguments "
79 "(excluding trailing NULLs)", 0);
80 if (!is_constructor) {
81 zval_ptr_dtor(return_value);
82 RETVAL_NULL();
83 }
84 return;
85 }
86
87 // argument parsing
88 if (variant <= 2) {
89 if (zend_parse_parameters_ex(zpp_flags, MIN(ZEND_NUM_ARGS(), 2),
90 "|z!s!", &tz_object, &locale, &locale_len) == FAILURE) {
91 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
92 "intlgregcal_create_instance: bad arguments", 0);
93 if (!is_constructor) {
94 zval_ptr_dtor(return_value);
95 RETVAL_NULL();
96 }
97 return;
98 }
99 }
100 if (variant > 2 && zend_parse_parameters_ex(zpp_flags, ZEND_NUM_ARGS(),
101 "lll|lll", &largs[0], &largs[1], &largs[2], &largs[3], &largs[4],
102 &largs[5]) == FAILURE) {
103 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
104 "intlgregcal_create_instance: bad arguments", 0);
105 if (!is_constructor) {
106 zval_ptr_dtor(return_value);
107 RETVAL_NULL();
108 }
109 return;
110 }
111
112 // instantion of ICU object
113 GregorianCalendar *gcal = NULL;
114
115 if (variant <= 2) {
116 // From timezone and locale (0 to 2 arguments)
117 TimeZone *tz = timezone_process_timezone_argument(tz_object, NULL,
118 "intlgregcal_create_instance");
119 if (tz == NULL) {
120 if (!EG(exception)) {
121 zend_throw_exception(IntlException_ce_ptr, "Constructor failed", 0);
122 }
123 if (!is_constructor) {
124 zval_ptr_dtor(return_value);
125 RETVAL_NULL();
126 }
127 return;
128 }
129 if (!locale) {
130 locale = const_cast<char*>(intl_locale_get_default());
131 }
132
133 gcal = new GregorianCalendar(tz, Locale::createFromName(locale),
134 status);
135 if (U_FAILURE(status)) {
136 intl_error_set(NULL, status, "intlgregcal_create_instance: error "
137 "creating ICU GregorianCalendar from time zone and locale", 0);
138 if (gcal) {
139 delete gcal;
140 }
141 delete tz;
142 if (!is_constructor) {
143 zval_ptr_dtor(return_value);
144 RETVAL_NULL();
145 }
146 return;
147 }
148 } else {
149 // From date/time (3, 5 or 6 arguments)
150 for (int i = 0; i < variant; i++) {
151 if (largs[i] < INT32_MIN || largs[i] > INT32_MAX) {
152 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
153 "intlgregcal_create_instance: at least one of the arguments"
154 " has an absolute value that is too large", 0);
155 if (!is_constructor) {
156 zval_ptr_dtor(return_value);
157 RETVAL_NULL();
158 }
159 return;
160 }
161 }
162
163 if (variant == 3) {
164 gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
165 (int32_t)largs[2], status);
166 } else if (variant == 5) {
167 gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
168 (int32_t)largs[2], (int32_t)largs[3], (int32_t)largs[4], status);
169 } else if (variant == 6) {
170 gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
171 (int32_t)largs[2], (int32_t)largs[3], (int32_t)largs[4], (int32_t)largs[5],
172 status);
173 }
174 if (U_FAILURE(status)) {
175 intl_error_set(NULL, status, "intlgregcal_create_instance: error "
176 "creating ICU GregorianCalendar from date", 0);
177 if (gcal) {
178 delete gcal;
179 }
180 if (!is_constructor) {
181 zval_ptr_dtor(return_value);
182 RETVAL_NULL();
183 }
184 return;
185 }
186
187 timelib_tzinfo *tzinfo = get_timezone_info();
188 #if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 42
189 UnicodeString tzstr = UnicodeString::fromUTF8(StringPiece(tzinfo->name));
190 #else
191 UnicodeString tzstr = UnicodeString(tzinfo->name,
192 strlen(tzinfo->name), US_INV);
193 #endif
194 if (tzstr.isBogus()) {
195 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
196 "intlgregcal_create_instance: could not create UTF-8 string "
197 "from PHP's default timezone name (see date_default_timezone_get())",
198 0);
199 delete gcal;
200 if (!is_constructor) {
201 zval_ptr_dtor(return_value);
202 RETVAL_NULL();
203 }
204 return;
205 }
206
207 TimeZone *tz = TimeZone::createTimeZone(tzstr);
208 gcal->adoptTimeZone(tz);
209 }
210
211 Calendar_object *co = Z_INTL_CALENDAR_P(return_value);
212 co->ucal = gcal;
213 }
214
PHP_FUNCTION(intlgregcal_create_instance)215 U_CFUNC PHP_FUNCTION(intlgregcal_create_instance)
216 {
217 intl_error_reset(NULL);
218
219 object_init_ex(return_value, GregorianCalendar_ce_ptr);
220 _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
221 }
222
PHP_METHOD(IntlGregorianCalendar,__construct)223 U_CFUNC PHP_METHOD(IntlGregorianCalendar, __construct)
224 {
225 zend_error_handling error_handling;
226
227 zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);
228 return_value = getThis();
229 _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
230 zend_restore_error_handling(&error_handling);
231 }
232
PHP_FUNCTION(intlgregcal_set_gregorian_change)233 U_CFUNC PHP_FUNCTION(intlgregcal_set_gregorian_change)
234 {
235 double date;
236 CALENDAR_METHOD_INIT_VARS;
237
238 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
239 "Od", &object, GregorianCalendar_ce_ptr, &date) == FAILURE) {
240 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
241 "intlgregcal_set_gregorian_change: bad arguments", 0);
242 RETURN_FALSE;
243 }
244
245 CALENDAR_METHOD_FETCH_OBJECT;
246
247 fetch_greg(co)->setGregorianChange(date, CALENDAR_ERROR_CODE(co));
248 INTL_METHOD_CHECK_STATUS(co, "intlgregcal_set_gregorian_change: error "
249 "calling ICU method");
250
251 RETURN_TRUE;
252 }
253
PHP_FUNCTION(intlgregcal_get_gregorian_change)254 U_CFUNC PHP_FUNCTION(intlgregcal_get_gregorian_change)
255 {
256 CALENDAR_METHOD_INIT_VARS;
257
258 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
259 "O", &object, GregorianCalendar_ce_ptr) == FAILURE) {
260 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
261 "intlgregcal_get_gregorian_change: bad arguments", 0);
262 RETURN_FALSE;
263 }
264
265 CALENDAR_METHOD_FETCH_OBJECT;
266
267 RETURN_DOUBLE((double)fetch_greg(co)->getGregorianChange());
268 }
269
PHP_FUNCTION(intlgregcal_is_leap_year)270 U_CFUNC PHP_FUNCTION(intlgregcal_is_leap_year)
271 {
272 zend_long year;
273 CALENDAR_METHOD_INIT_VARS;
274
275 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
276 "Ol", &object, GregorianCalendar_ce_ptr, &year) == FAILURE) {
277 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
278 "intlgregcal_is_leap_year: bad arguments", 0);
279 RETURN_FALSE;
280 }
281
282 if (year < INT32_MIN || year > INT32_MAX) {
283 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
284 "intlgregcal_is_leap_year: year out of bounds", 0);
285 RETURN_FALSE;
286 }
287
288 CALENDAR_METHOD_FETCH_OBJECT;
289
290 RETURN_BOOL((int)fetch_greg(co)->isLeapYear((int32_t)year));
291 }
292