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