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