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