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 #define __STDC_FORMAT_MACROS
22 #include <inttypes.h>
23 
24 #include "../intl_cppshims.h"
25 
26 #include <unicode/locid.h>
27 #include <unicode/calendar.h>
28 #include <unicode/ustring.h>
29 
30 #include "../intl_convertcpp.h"
31 #include "../common/common_date.h"
32 
33 extern "C" {
34 #include "../php_intl.h"
35 #define USE_TIMEZONE_POINTER 1
36 #include "../timezone/timezone_class.h"
37 #define USE_CALENDAR_POINTER 1
38 #include "calendar_class.h"
39 #include "../intl_convert.h"
40 #include <zend_exceptions.h>
41 #include <zend_interfaces.h>
42 #include <ext/date/php_date.h>
43 }
44 #include "../common/common_enum.h"
45 
46 using icu::Locale;
47 
PHP_METHOD(IntlCalendar,__construct)48 U_CFUNC PHP_METHOD(IntlCalendar, __construct)
49 {
50 	zend_throw_exception( NULL,
51 		"An object of this type cannot be created with the new operator",
52 		0 );
53 }
54 
PHP_FUNCTION(intlcal_create_instance)55 U_CFUNC PHP_FUNCTION(intlcal_create_instance)
56 {
57 	zval		*zv_timezone	= NULL;
58 	const char	*locale_str		= NULL;
59 	size_t			dummy;
60 	TimeZone	*timeZone;
61 	UErrorCode	status			= U_ZERO_ERROR;
62 	intl_error_reset(NULL);
63 
64 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|zs!",
65 			&zv_timezone, &locale_str, &dummy) == FAILURE) {
66 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
67 			"intlcal_create_calendar: bad arguments", 0);
68 		RETURN_NULL();
69 	}
70 
71 	timeZone = timezone_process_timezone_argument(zv_timezone, NULL,
72 		"intlcal_create_instance");
73 	if (timeZone == NULL) {
74 		RETURN_NULL();
75 	}
76 
77 	if (!locale_str) {
78 		locale_str = intl_locale_get_default();
79 	}
80 
81 	Calendar *cal = Calendar::createInstance(timeZone,
82 		Locale::createFromName(locale_str), status);
83 	if (cal == NULL) {
84 		delete timeZone;
85 		intl_error_set(NULL, status, "Error creating ICU Calendar object", 0);
86 		RETURN_NULL();
87 	}
88 
89 	calendar_object_create(return_value, cal);
90 }
91 
92 class BugStringCharEnumeration : public StringEnumeration
93 {
94 public:
BugStringCharEnumeration(UEnumeration * _uenum)95 	explicit BugStringCharEnumeration(UEnumeration* _uenum) : uenum(_uenum) {}
96 
~BugStringCharEnumeration()97 	~BugStringCharEnumeration()
98 	{
99 		uenum_close(uenum);
100 	}
101 
count(UErrorCode & status) const102 	int32_t count(UErrorCode& status) const {
103 		return uenum_count(uenum, &status);
104 	}
105 
snext(UErrorCode & status)106 	virtual const UnicodeString* snext(UErrorCode& status)
107 	{
108 		int32_t length;
109 		const UChar* str = uenum_unext(uenum, &length, &status);
110 		if (str == 0 || U_FAILURE(status)) {
111 			return 0;
112 		}
113 		return &unistr.setTo(str, length);
114 	}
115 
next(int32_t * resultLength,UErrorCode & status)116 	virtual const char* next(int32_t *resultLength, UErrorCode &status)
117 	{
118 		int32_t length = -1;
119 		const char* str = uenum_next(uenum, &length, &status);
120 		if (str == 0 || U_FAILURE(status)) {
121 			return 0;
122 		}
123 		if (resultLength) {
124 			//the bug is that uenum_next doesn't set the length
125 			*resultLength = (length == -1) ? (int32_t)strlen(str) : length;
126 		}
127 
128 		return str;
129 	}
130 
reset(UErrorCode & status)131 	void reset(UErrorCode& status)
132 	{
133 		uenum_reset(uenum, &status);
134 	}
135 
136 	virtual UClassID getDynamicClassID() const;
137 
138 	static UClassID U_EXPORT2 getStaticClassID();
139 
140 private:
141 	UEnumeration *uenum;
142 };
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BugStringCharEnumeration)143 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(BugStringCharEnumeration)
144 
145 U_CFUNC PHP_FUNCTION(intlcal_get_keyword_values_for_locale)
146 {
147 	UErrorCode	status = U_ZERO_ERROR;
148 	char		*key,
149 				*locale;
150 	size_t			key_len,
151 				locale_len;
152 	zend_bool	commonly_used;
153 	intl_error_reset(NULL);
154 
155 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssb",
156 			&key, &key_len, &locale, &locale_len, &commonly_used) == FAILURE) {
157 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
158 			"intlcal_get_keyword_values_for_locale: bad arguments", 0);
159 		RETURN_FALSE;
160 	}
161 
162 	//does not work; see ICU bug 9194
163 #if 0
164 	StringEnumeration *se = Calendar::getKeywordValuesForLocale(key,
165 		Locale::createFromName(locale), (UBool)commonly_used,
166 		status);
167 	if (se == NULL) {
168 		intl_error_set(NULL, status, "intlcal_get_keyword_values_for_locale: "
169 			"error calling underlying method", 0);
170 		RETURN_FALSE;
171 	}
172 #else
173     UEnumeration *uenum = ucal_getKeywordValuesForLocale(
174 		key, locale, !!commonly_used, &status);
175     if (U_FAILURE(status)) {
176         uenum_close(uenum);
177 		intl_error_set(NULL, status, "intlcal_get_keyword_values_for_locale: "
178 			"error calling underlying method", 0);
179         RETURN_FALSE;
180     }
181 
182     StringEnumeration *se = new BugStringCharEnumeration(uenum);
183 #endif
184 
185 	IntlIterator_from_StringEnumeration(se, return_value);
186 }
187 
PHP_FUNCTION(intlcal_get_now)188 U_CFUNC PHP_FUNCTION(intlcal_get_now)
189 {
190 	intl_error_reset(NULL);
191 
192 	if (zend_parse_parameters_none() == FAILURE) {
193 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
194 			"intlcal_get_now: bad arguments", 0);
195 		RETURN_FALSE;
196 	}
197 
198 	RETURN_DOUBLE((double)Calendar::getNow());
199 }
200 
PHP_FUNCTION(intlcal_get_available_locales)201 U_CFUNC PHP_FUNCTION(intlcal_get_available_locales)
202 {
203 	intl_error_reset(NULL);
204 
205 	if (zend_parse_parameters_none() == FAILURE) {
206 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
207 			"intlcal_get_available_locales: bad arguments", 0);
208 		RETURN_FALSE;
209 	}
210 
211 	int32_t count;
212 	const Locale *availLocales = Calendar::getAvailableLocales(count);
213 	array_init(return_value);
214 	for (int i = 0; i < count; i++) {
215 		Locale locale = availLocales[i];
216 		add_next_index_string(return_value, locale.getName());
217 	}
218 }
219 
_php_intlcal_field_uec_ret_in32t_method(int32_t (Calendar::* func)(UCalendarDateFields,UErrorCode &)const,const char * method_name,INTERNAL_FUNCTION_PARAMETERS)220 static void _php_intlcal_field_uec_ret_in32t_method(
221 		int32_t (Calendar::*func)(UCalendarDateFields, UErrorCode&) const,
222 		const char *method_name,
223 		INTERNAL_FUNCTION_PARAMETERS)
224 {
225 	zend_long	field;
226 	char	*message;
227 	CALENDAR_METHOD_INIT_VARS;
228 
229 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
230 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
231 		spprintf(&message, 0, "%s: bad arguments", method_name);
232 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
233 		efree(message);
234 		RETURN_FALSE;
235 	}
236 
237 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
238 		spprintf(&message, 0, "%s: invalid field", method_name);
239 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
240 		efree(message);
241 		RETURN_FALSE;
242 	}
243 
244 	CALENDAR_METHOD_FETCH_OBJECT;
245 
246 	int32_t result = (co->ucal->*func)(
247 		(UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
248 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
249 
250 	RETURN_LONG((zend_long)result);
251 }
252 
PHP_FUNCTION(intlcal_get)253 U_CFUNC PHP_FUNCTION(intlcal_get)
254 {
255 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::get,
256 		"intlcal_get", INTERNAL_FUNCTION_PARAM_PASSTHRU);
257 }
258 
PHP_FUNCTION(intlcal_get_time)259 U_CFUNC PHP_FUNCTION(intlcal_get_time)
260 {
261 	CALENDAR_METHOD_INIT_VARS;
262 
263 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
264 			&object, Calendar_ce_ptr) == FAILURE) {
265 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
266 			"intlcal_get_time: bad arguments", 0);
267 		RETURN_FALSE;
268 	}
269 
270 	CALENDAR_METHOD_FETCH_OBJECT;
271 
272 	UDate result = co->ucal->getTime(CALENDAR_ERROR_CODE(co));
273 	INTL_METHOD_CHECK_STATUS(co,
274 		"intlcal_get_time: error calling ICU Calendar::getTime");
275 
276 	RETURN_DOUBLE((double)result);
277 }
278 
PHP_FUNCTION(intlcal_set_time)279 U_CFUNC PHP_FUNCTION(intlcal_set_time)
280 {
281 	double	time_arg;
282 	CALENDAR_METHOD_INIT_VARS;
283 
284 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Od",
285 			&object, Calendar_ce_ptr, &time_arg) == FAILURE) {
286 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
287 			"intlcal_set_time: bad arguments", 0);
288 		RETURN_FALSE;
289 	}
290 
291 	CALENDAR_METHOD_FETCH_OBJECT;
292 
293 	co->ucal->setTime((UDate)time_arg, CALENDAR_ERROR_CODE(co));
294 	INTL_METHOD_CHECK_STATUS(co, "Call to underlying method failed");
295 
296 	RETURN_TRUE;
297 }
298 
PHP_FUNCTION(intlcal_add)299 U_CFUNC PHP_FUNCTION(intlcal_add)
300 {
301 	zend_long	field,
302 			amount;
303 	CALENDAR_METHOD_INIT_VARS;
304 
305 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
306 			"Oll", &object, Calendar_ce_ptr, &field, &amount) == FAILURE) {
307 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
308 			"intlcal_add: bad arguments", 0);
309 		RETURN_FALSE;
310 	}
311 
312 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
313 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
314 			"intlcal_add: invalid field", 0);
315 		RETURN_FALSE;
316 	}
317 	if (amount < INT32_MIN || amount > INT32_MAX) {
318 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
319 			"intlcal_add: amount out of bounds", 0);
320 		RETURN_FALSE;
321 	}
322 
323 	CALENDAR_METHOD_FETCH_OBJECT;
324 
325 	co->ucal->add((UCalendarDateFields)field, (int32_t)amount, CALENDAR_ERROR_CODE(co));
326 	INTL_METHOD_CHECK_STATUS(co, "intlcal_add: Call to underlying method failed");
327 
328 	RETURN_TRUE;
329 }
330 
PHP_FUNCTION(intlcal_set_time_zone)331 U_CFUNC PHP_FUNCTION(intlcal_set_time_zone)
332 {
333 	zval			*zv_timezone;
334 	TimeZone		*timeZone;
335 	CALENDAR_METHOD_INIT_VARS;
336 
337 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
338 			"Oz!", &object, Calendar_ce_ptr, &zv_timezone) == FAILURE) {
339 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
340 			"intlcal_set_time_zone: bad arguments", 0);
341 		RETURN_FALSE;
342 	}
343 
344 	CALENDAR_METHOD_FETCH_OBJECT;
345 
346 	if (zv_timezone == NULL) {
347 		RETURN_TRUE; /* the method does nothing if passed null */
348 	}
349 
350 	timeZone = timezone_process_timezone_argument(zv_timezone,
351 			CALENDAR_ERROR_P(co), "intlcal_set_time_zone");
352 	if (timeZone == NULL) {
353 		RETURN_FALSE;
354 	}
355 
356 	co->ucal->adoptTimeZone(timeZone);
357 
358 	RETURN_TRUE;
359 }
360 
361 
_php_intlcal_before_after(UBool (Calendar::* func)(const Calendar &,UErrorCode &)const,INTERNAL_FUNCTION_PARAMETERS)362 static void _php_intlcal_before_after(
363 		UBool (Calendar::*func)(const Calendar&, UErrorCode&) const,
364 		INTERNAL_FUNCTION_PARAMETERS)
365 {
366 	zval			*when_object;
367 	Calendar_object	*when_co;
368 	CALENDAR_METHOD_INIT_VARS;
369 
370 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
371 			"OO", &object, Calendar_ce_ptr, &when_object, Calendar_ce_ptr)
372 			== FAILURE) {
373 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
374 			"intlcal_before/after: bad arguments", 0);
375 		RETURN_FALSE;
376 	}
377 
378 	CALENDAR_METHOD_FETCH_OBJECT;
379 
380 	when_co = Z_INTL_CALENDAR_P(when_object);
381 	if (when_co->ucal == NULL) {
382 		intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR,
383 			"intlcal_before/after: Other IntlCalendar was unconstructed", 0);
384 		RETURN_FALSE;
385 	}
386 
387 	UBool res = (co->ucal->*func)(*when_co->ucal, CALENDAR_ERROR_CODE(co));
388 	INTL_METHOD_CHECK_STATUS(co, "intlcal_before/after: Error calling ICU method");
389 
390 	RETURN_BOOL((int)res);
391 }
392 
PHP_FUNCTION(intlcal_after)393 U_CFUNC PHP_FUNCTION(intlcal_after)
394 {
395 	_php_intlcal_before_after(&Calendar::after, INTERNAL_FUNCTION_PARAM_PASSTHRU);
396 }
397 
PHP_FUNCTION(intlcal_before)398 U_CFUNC PHP_FUNCTION(intlcal_before)
399 {
400 	_php_intlcal_before_after(&Calendar::before, INTERNAL_FUNCTION_PARAM_PASSTHRU);
401 }
402 
PHP_FUNCTION(intlcal_set)403 U_CFUNC PHP_FUNCTION(intlcal_set)
404 {
405 	zend_long	arg1, arg2, arg3, arg4, arg5, arg6;
406 	zval	args_a[7] = {0},
407 			*args = args_a;
408 	int		i;
409 	int		variant; /* number of args of the set() overload */
410 	CALENDAR_METHOD_INIT_VARS;
411 
412 	object = getThis();
413 
414 	/* must come before zpp because zpp would convert the args in the stack to 0 */
415 	if (ZEND_NUM_ARGS() > (object ? 6 : 7) ||
416 				zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
417 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
418 			"intlcal_set: too many arguments", 0);
419 		RETURN_FALSE;
420 	}
421 	if (!object) {
422 		args++;
423 	}
424 	variant = ZEND_NUM_ARGS() - (object ? 0 : 1);
425 	while (variant > 2 && Z_TYPE(args[variant - 1]) == IS_NULL) {
426 		variant--;
427 	}
428 
429 	if (variant == 4 ||
430 			zend_parse_method_parameters(ZEND_NUM_ARGS(), object,
431 			"Oll|llll",	&object, Calendar_ce_ptr, &arg1, &arg2, &arg3, &arg4,
432 			&arg5, &arg6) == FAILURE) {
433 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
434 			"intlcal_set: bad arguments", 0);
435 		RETURN_FALSE;
436 	}
437 
438 	for (i = 0; i < variant; i++) {
439 		if (Z_LVAL(args[i]) < INT32_MIN || Z_LVAL(args[i]) > INT32_MAX) {
440 			intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
441 				"intlcal_set: at least one of the arguments has an absolute "
442 				"value that is too large", 0);
443 			RETURN_FALSE;
444 		}
445 	}
446 
447 	if (variant == 2 && (arg1 < 0 || arg1 >= UCAL_FIELD_COUNT)) {
448 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
449 			"intlcal_set: invalid field", 0);
450 		RETURN_FALSE;
451 	}
452 
453 	CALENDAR_METHOD_FETCH_OBJECT;
454 
455 	if (variant == 2) {
456 		co->ucal->set((UCalendarDateFields)arg1, (int32_t)arg2);
457 	} else if (variant == 3) {
458 		co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3);
459 	} else if (variant == 5) {
460 		co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3, (int32_t)arg4, (int32_t)arg5);
461 	} else if (variant == 6) {
462 		co->ucal->set((int32_t)arg1, (int32_t)arg2, (int32_t)arg3, (int32_t)arg4, (int32_t)arg5, (int32_t)arg6);
463 	}
464 
465 	RETURN_TRUE;
466 }
467 
PHP_FUNCTION(intlcal_roll)468 U_CFUNC PHP_FUNCTION(intlcal_roll)
469 {
470 	zend_long		field,
471 				value;
472 	zval		args_a[3]		 = {0},
473 				*args			 = args_a;
474 	zend_bool	bool_variant_val = (zend_bool)-1;
475 	CALENDAR_METHOD_INIT_VARS;
476 
477 	object = getThis();
478 
479 	if (ZEND_NUM_ARGS() > (object ? 2 :3) ||
480 			zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) {
481 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
482 			"intlcal_set: too many arguments", 0);
483 		RETURN_FALSE;
484 	}
485 	if (!object) {
486 		args++;
487 	}
488 	if (!Z_ISUNDEF(args[1]) && (Z_TYPE(args[1]) == IS_TRUE || Z_TYPE(args[1]) == IS_FALSE)) {
489 		if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object,
490 				"Olb", &object, Calendar_ce_ptr, &field, &bool_variant_val)
491 				== FAILURE) {
492 			intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
493 				"intlcal_roll: bad arguments", 0);
494 			RETURN_FALSE;
495 		}
496 		bool_variant_val = Z_TYPE(args[1]) == IS_TRUE? 1 : 0;
497 	} else if (zend_parse_method_parameters(ZEND_NUM_ARGS(), object,
498 			"Oll", &object, Calendar_ce_ptr, &field, &value) == FAILURE) {
499 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
500 			"intlcal_roll: bad arguments", 0);
501 		RETURN_FALSE;
502 	}
503 
504 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
505 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
506 			"intlcal_roll: invalid field", 0);
507 		RETURN_FALSE;
508 	}
509 	if (bool_variant_val == (zend_bool)-1 &&
510 			(value < INT32_MIN || value > INT32_MAX)) {
511 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
512 			"intlcal_roll: value out of bounds", 0);
513 		RETURN_FALSE;
514 	}
515 
516 	CALENDAR_METHOD_FETCH_OBJECT;
517 
518 	if (bool_variant_val != (zend_bool)-1) {
519 		co->ucal->roll((UCalendarDateFields)field, (UBool)bool_variant_val,
520 			CALENDAR_ERROR_CODE(co));
521 	} else {
522 		co->ucal->roll((UCalendarDateFields)field, (int32_t)value,
523 			CALENDAR_ERROR_CODE(co));
524 	}
525 	INTL_METHOD_CHECK_STATUS(co, "intlcal_roll: Error calling ICU Calendar::roll");
526 
527 	RETURN_TRUE;
528 }
529 
PHP_FUNCTION(intlcal_clear)530 U_CFUNC PHP_FUNCTION(intlcal_clear)
531 {
532 	zend_long field;
533 	zend_bool field_is_null = 1;
534 	CALENDAR_METHOD_INIT_VARS;
535 
536 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(),
537 			getThis(), "O|l!", &object, Calendar_ce_ptr, &field, &field_is_null) == FAILURE) {
538 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
539 			"intlcal_clear: bad arguments", 0);
540 		RETURN_FALSE;
541 	}
542 
543 	CALENDAR_METHOD_FETCH_OBJECT;
544 
545 	if (field_is_null) {
546 		co->ucal->clear();
547 	} else {
548 		if (field < 0 || field >= UCAL_FIELD_COUNT) {
549 			intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
550 				"intlcal_clear: invalid field", 0);
551 			RETURN_FALSE;
552 		}
553 
554 		co->ucal->clear((UCalendarDateFields)field);
555 	}
556 
557 	RETURN_TRUE;
558 }
559 
PHP_FUNCTION(intlcal_field_difference)560 U_CFUNC PHP_FUNCTION(intlcal_field_difference)
561 {
562 	zend_long	field;
563 	double	when;
564 	CALENDAR_METHOD_INIT_VARS;
565 
566 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
567 			"Odl", &object, Calendar_ce_ptr, &when, &field)	== FAILURE) {
568 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
569 			"intlcal_field_difference: bad arguments", 0);
570 		RETURN_FALSE;
571 	}
572 
573 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
574 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
575 			"intlcal_field_difference: invalid field", 0);
576 		RETURN_FALSE;
577 	}
578 
579 	CALENDAR_METHOD_FETCH_OBJECT;
580 
581 	int32_t result = co->ucal->fieldDifference((UDate)when,
582 		(UCalendarDateFields)field, CALENDAR_ERROR_CODE(co));
583 	INTL_METHOD_CHECK_STATUS(co,
584 		"intlcal_field_difference: Call to ICU method has failed");
585 
586 	RETURN_LONG((zend_long)result);
587 }
588 
PHP_FUNCTION(intlcal_get_actual_maximum)589 U_CFUNC PHP_FUNCTION(intlcal_get_actual_maximum)
590 {
591 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMaximum,
592 		"intlcal_get_actual_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
593 }
594 
PHP_FUNCTION(intlcal_get_actual_minimum)595 U_CFUNC PHP_FUNCTION(intlcal_get_actual_minimum)
596 {
597 	_php_intlcal_field_uec_ret_in32t_method(&Calendar::getActualMinimum,
598 		"intlcal_get_actual_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
599 }
600 
PHP_FUNCTION(intlcal_get_day_of_week_type)601 U_CFUNC PHP_FUNCTION(intlcal_get_day_of_week_type)
602 {
603 	zend_long	dow;
604 	CALENDAR_METHOD_INIT_VARS;
605 
606 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
607 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
608 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
609 			"intlcal_get_day_of_week_type: bad arguments", 0);
610 		RETURN_FALSE;
611 	}
612 
613 	if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
614 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
615 			"intlcal_get_day_of_week_type: invalid day of week", 0);
616 		RETURN_FALSE;
617 	}
618 
619 	CALENDAR_METHOD_FETCH_OBJECT;
620 
621 	int32_t result = co->ucal->getDayOfWeekType(
622 		(UCalendarDaysOfWeek)dow, CALENDAR_ERROR_CODE(co));
623 	INTL_METHOD_CHECK_STATUS(co,
624 		"intlcal_get_day_of_week_type: Call to ICU method has failed");
625 
626 	RETURN_LONG((zend_long)result);
627 }
628 
PHP_FUNCTION(intlcal_get_first_day_of_week)629 U_CFUNC PHP_FUNCTION(intlcal_get_first_day_of_week)
630 {
631 	CALENDAR_METHOD_INIT_VARS;
632 
633 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
634 			"O", &object, Calendar_ce_ptr) == FAILURE) {
635 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
636 			"intlcal_get_first_day_of_week: bad arguments", 0);
637 		RETURN_FALSE;
638 	}
639 
640 	CALENDAR_METHOD_FETCH_OBJECT;
641 
642 	int32_t result = co->ucal->getFirstDayOfWeek(CALENDAR_ERROR_CODE(co));
643 	INTL_METHOD_CHECK_STATUS(co,
644 		"intlcal_get_first_day_of_week: Call to ICU method has failed");
645 
646 	RETURN_LONG((zend_long)result);
647 }
648 
_php_intlcal_field_ret_in32t_method(int32_t (Calendar::* func)(UCalendarDateFields)const,const char * method_name,INTERNAL_FUNCTION_PARAMETERS)649 static void _php_intlcal_field_ret_in32t_method(
650 		int32_t (Calendar::*func)(UCalendarDateFields) const,
651 		const char *method_name,
652 		INTERNAL_FUNCTION_PARAMETERS)
653 {
654 	zend_long	field;
655 	char	*message;
656 	CALENDAR_METHOD_INIT_VARS;
657 
658 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
659 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
660 		spprintf(&message, 0, "%s: bad arguments", method_name);
661 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
662 		efree(message);
663 		RETURN_FALSE;
664 	}
665 
666 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
667 		spprintf(&message, 0, "%s: invalid field", method_name);
668 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
669 		efree(message);
670 		RETURN_FALSE;
671 	}
672 
673 	CALENDAR_METHOD_FETCH_OBJECT;
674 
675 	int32_t result = (co->ucal->*func)((UCalendarDateFields)field);
676 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
677 
678 	RETURN_LONG((zend_long)result);
679 }
680 
PHP_FUNCTION(intlcal_get_greatest_minimum)681 U_CFUNC PHP_FUNCTION(intlcal_get_greatest_minimum)
682 {
683 	_php_intlcal_field_ret_in32t_method(&Calendar::getGreatestMinimum,
684 		"intlcal_get_greatest_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
685 }
686 
PHP_FUNCTION(intlcal_get_least_maximum)687 U_CFUNC PHP_FUNCTION(intlcal_get_least_maximum)
688 {
689 	_php_intlcal_field_ret_in32t_method(&Calendar::getLeastMaximum,
690 		"intlcal_get_least_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
691 }
692 
PHP_FUNCTION(intlcal_get_locale)693 U_CFUNC PHP_FUNCTION(intlcal_get_locale)
694 {
695 	zend_long	locale_type;
696 	CALENDAR_METHOD_INIT_VARS;
697 
698 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
699 			"Ol", &object, Calendar_ce_ptr, &locale_type) == FAILURE) {
700 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
701 			"intlcal_get_locale: bad arguments", 0);
702 		RETURN_FALSE;
703 	}
704 
705 	if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) {
706 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
707 			"intlcal_get_locale: invalid locale type", 0);
708 		RETURN_FALSE;
709 	}
710 
711 	CALENDAR_METHOD_FETCH_OBJECT;
712 
713 	Locale locale = co->ucal->getLocale((ULocDataLocaleType)locale_type,
714 		CALENDAR_ERROR_CODE(co));
715 	INTL_METHOD_CHECK_STATUS(co,
716 		"intlcal_get_locale: Call to ICU method has failed");
717 
718 	RETURN_STRING(locale.getName());
719 }
720 
PHP_FUNCTION(intlcal_get_maximum)721 U_CFUNC PHP_FUNCTION(intlcal_get_maximum)
722 {
723 	_php_intlcal_field_ret_in32t_method(&Calendar::getMaximum,
724 		"intlcal_get_maximum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
725 }
726 
PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)727 U_CFUNC PHP_FUNCTION(intlcal_get_minimal_days_in_first_week)
728 {
729 	CALENDAR_METHOD_INIT_VARS;
730 
731 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
732 			"O", &object, Calendar_ce_ptr) == FAILURE) {
733 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
734 			"intlcal_get_minimal_days_in_first_week: bad arguments", 0);
735 		RETURN_FALSE;
736 	}
737 
738 	CALENDAR_METHOD_FETCH_OBJECT;
739 
740 	uint8_t result = co->ucal->getMinimalDaysInFirstWeek();
741 	INTL_METHOD_CHECK_STATUS(co,
742 		"intlcal_get_first_day_of_week: Call to ICU method has failed");
743 
744 	RETURN_LONG((zend_long)result);
745 }
746 
PHP_FUNCTION(intlcal_get_minimum)747 U_CFUNC PHP_FUNCTION(intlcal_get_minimum)
748 {
749 	_php_intlcal_field_ret_in32t_method(&Calendar::getMinimum,
750 		"intlcal_get_minimum", INTERNAL_FUNCTION_PARAM_PASSTHRU);
751 }
752 
PHP_FUNCTION(intlcal_get_time_zone)753 U_CFUNC PHP_FUNCTION(intlcal_get_time_zone)
754 {
755 	CALENDAR_METHOD_INIT_VARS;
756 
757 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
758 			"O", &object, Calendar_ce_ptr) == FAILURE) {
759 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
760 			"intlcal_get_time_zone: bad arguments", 0);
761 		RETURN_FALSE;
762 	}
763 
764 	CALENDAR_METHOD_FETCH_OBJECT;
765 
766 	TimeZone *tz = co->ucal->getTimeZone().clone();
767 	if (tz == NULL) {
768 		intl_errors_set(CALENDAR_ERROR_P(co), U_MEMORY_ALLOCATION_ERROR,
769 			"intlcal_get_time_zone: could not clone TimeZone", 0);
770 		RETURN_FALSE;
771 	}
772 
773 	timezone_object_construct(tz, return_value, 1);
774 }
775 
PHP_FUNCTION(intlcal_get_type)776 U_CFUNC PHP_FUNCTION(intlcal_get_type)
777 {
778 	CALENDAR_METHOD_INIT_VARS;
779 
780 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
781 			"O", &object, Calendar_ce_ptr) == FAILURE) {
782 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
783 			"intlcal_get_type: bad arguments", 0);
784 		RETURN_FALSE;
785 	}
786 
787 	CALENDAR_METHOD_FETCH_OBJECT;
788 
789 	RETURN_STRING(co->ucal->getType());
790 }
791 
PHP_FUNCTION(intlcal_get_weekend_transition)792 U_CFUNC PHP_FUNCTION(intlcal_get_weekend_transition)
793 {
794 	zend_long	dow;
795 	CALENDAR_METHOD_INIT_VARS;
796 
797 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
798 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
799 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
800 			"intlcal_get_weekend_transition: bad arguments", 0);
801 		RETURN_FALSE;
802 	}
803 
804 	if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
805 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
806 			"intlcal_get_weekend_transition: invalid day of week", 0);
807 		RETURN_FALSE;
808 	}
809 
810 	CALENDAR_METHOD_FETCH_OBJECT;
811 
812 	int32_t res = co->ucal->getWeekendTransition((UCalendarDaysOfWeek)dow,
813 		CALENDAR_ERROR_CODE(co));
814 	INTL_METHOD_CHECK_STATUS(co, "intlcal_get_weekend_transition: "
815 		"Error calling ICU method");
816 
817 	RETURN_LONG((zend_long)res);
818 }
819 
PHP_FUNCTION(intlcal_in_daylight_time)820 U_CFUNC PHP_FUNCTION(intlcal_in_daylight_time)
821 {
822 	CALENDAR_METHOD_INIT_VARS;
823 
824 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
825 			"O", &object, Calendar_ce_ptr) == FAILURE) {
826 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
827 			"intlcal_in_daylight_time: bad arguments", 0);
828 		RETURN_FALSE;
829 	}
830 
831 	CALENDAR_METHOD_FETCH_OBJECT;
832 
833 	UBool ret = co->ucal->inDaylightTime(CALENDAR_ERROR_CODE(co));
834 	INTL_METHOD_CHECK_STATUS(co, "intlcal_in_daylight_time: "
835 		"Error calling ICU method");
836 
837 	RETURN_BOOL((int)ret);
838 }
839 
PHP_FUNCTION(intlcal_is_equivalent_to)840 U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to)
841 {
842 	zval			*other_object;
843 	Calendar_object *other_co;
844 	CALENDAR_METHOD_INIT_VARS;
845 
846 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
847 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
848 			== FAILURE) {
849 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
850 			"intlcal_is_equivalent_to: bad arguments", 0);
851 		RETURN_FALSE;
852 	}
853 
854 	other_co = Z_INTL_CALENDAR_P(other_object);
855 	if (other_co->ucal == NULL) {
856 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "intlcal_is_equivalent_to:"
857 			" Other IntlCalendar is unconstructed", 0);
858 		RETURN_FALSE;
859 	}
860 
861 	CALENDAR_METHOD_FETCH_OBJECT;
862 
863 	RETURN_BOOL((int)co->ucal->isEquivalentTo(*other_co->ucal));
864 }
865 
PHP_FUNCTION(intlcal_is_lenient)866 U_CFUNC PHP_FUNCTION(intlcal_is_lenient)
867 {
868 	CALENDAR_METHOD_INIT_VARS;
869 
870 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
871 			"O", &object, Calendar_ce_ptr) == FAILURE) {
872 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
873 			"intlcal_is_lenient: bad arguments", 0);
874 		RETURN_FALSE;
875 	}
876 
877 	CALENDAR_METHOD_FETCH_OBJECT;
878 
879 	RETURN_BOOL((int)co->ucal->isLenient());
880 }
881 
PHP_FUNCTION(intlcal_is_set)882 U_CFUNC PHP_FUNCTION(intlcal_is_set)
883 {
884 	zend_long field;
885 	CALENDAR_METHOD_INIT_VARS;
886 
887 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
888 			"Ol", &object, Calendar_ce_ptr, &field) == FAILURE) {
889 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
890 			"intlcal_is_set: bad arguments", 0);
891 		RETURN_FALSE;
892 	}
893 
894 	if (field < 0 || field >= UCAL_FIELD_COUNT) {
895 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
896 			"intlcal_is_set: invalid field", 0);
897 		RETURN_FALSE;
898 	}
899 
900 	CALENDAR_METHOD_FETCH_OBJECT;
901 
902 	RETURN_BOOL((int)co->ucal->isSet((UCalendarDateFields)field));
903 }
904 
PHP_FUNCTION(intlcal_is_weekend)905 U_CFUNC PHP_FUNCTION(intlcal_is_weekend)
906 {
907 	double date;
908 	zend_bool date_is_null = 1;
909 	CALENDAR_METHOD_INIT_VARS;
910 
911 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
912 				"O|d!", &object, Calendar_ce_ptr, &date, &date_is_null) == FAILURE) {
913 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
914 			"intlcal_is_weekend: bad arguments", 0);
915 		RETURN_FALSE;
916 	}
917 
918 	CALENDAR_METHOD_FETCH_OBJECT;
919 
920 	if (date_is_null) {
921 		RETURN_BOOL((int)co->ucal->isWeekend());
922 	} else {
923 		UBool ret = co->ucal->isWeekend((UDate)date, CALENDAR_ERROR_CODE(co));
924 		INTL_METHOD_CHECK_STATUS(co, "intlcal_is_weekend: "
925 			"Error calling ICU method");
926 		RETURN_BOOL((int)ret);
927 	}
928 }
929 
930 
PHP_FUNCTION(intlcal_set_first_day_of_week)931 U_CFUNC PHP_FUNCTION(intlcal_set_first_day_of_week)
932 {
933 	zend_long	dow;
934 	CALENDAR_METHOD_INIT_VARS;
935 
936 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
937 			"Ol", &object, Calendar_ce_ptr, &dow) == FAILURE) {
938 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
939 			"intlcal_set_first_day_of_week: bad arguments", 0);
940 		RETURN_FALSE;
941 	}
942 
943 	if (dow < UCAL_SUNDAY || dow > UCAL_SATURDAY) {
944 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
945 			"intlcal_set_first_day_of_week: invalid day of week", 0);
946 		RETURN_FALSE;
947 	}
948 
949 	CALENDAR_METHOD_FETCH_OBJECT;
950 
951 	co->ucal->setFirstDayOfWeek((UCalendarDaysOfWeek)dow);
952 
953 	RETURN_TRUE;
954 }
955 
PHP_FUNCTION(intlcal_set_lenient)956 U_CFUNC PHP_FUNCTION(intlcal_set_lenient)
957 {
958 	zend_bool is_lenient;
959 	CALENDAR_METHOD_INIT_VARS;
960 
961 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
962 			"Ob", &object, Calendar_ce_ptr, &is_lenient) == FAILURE) {
963 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
964 			"intlcal_set_lenient: bad arguments", 0);
965 		RETURN_FALSE;
966 	}
967 
968 	CALENDAR_METHOD_FETCH_OBJECT;
969 
970 	co->ucal->setLenient((UBool) is_lenient);
971 
972 	RETURN_TRUE;
973 }
974 
PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)975 U_CFUNC PHP_FUNCTION(intlcal_set_minimal_days_in_first_week)
976 {
977 	zend_long	num_days;
978 	CALENDAR_METHOD_INIT_VARS;
979 
980 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
981 			"Ol", &object, Calendar_ce_ptr, &num_days) == FAILURE) {
982 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
983 			"intlcal_set_minimal_days_in_first_week: bad arguments", 0);
984 		RETURN_FALSE;
985 	}
986 
987 	if (num_days < 1 || num_days > 7) {
988 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
989 			"intlcal_set_minimal_days_in_first_week: invalid number of days; "
990 			"must be between 1 and 7", 0);
991 		RETURN_FALSE;
992 	}
993 
994 	CALENDAR_METHOD_FETCH_OBJECT;
995 
996 	co->ucal->setMinimalDaysInFirstWeek((uint8_t)num_days);
997 
998 	RETURN_TRUE;
999 }
1000 
PHP_FUNCTION(intlcal_equals)1001 U_CFUNC PHP_FUNCTION(intlcal_equals)
1002 {
1003 	zval			*other_object;
1004 	Calendar_object	*other_co;
1005 	CALENDAR_METHOD_INIT_VARS;
1006 
1007 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1008 			"OO", &object, Calendar_ce_ptr, &other_object, Calendar_ce_ptr)
1009 			== FAILURE) {
1010 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1011 			"intlcal_equals: bad arguments", 0);
1012 		RETURN_FALSE;
1013 	}
1014 
1015 	CALENDAR_METHOD_FETCH_OBJECT;
1016 	other_co = Z_INTL_CALENDAR_P(other_object);
1017 	if (other_co->ucal == NULL) {
1018 		intl_errors_set(&co->err, U_ILLEGAL_ARGUMENT_ERROR,
1019 			"intlcal_equals: The second IntlCalendar is unconstructed", 0);
1020 		RETURN_FALSE;
1021 	}
1022 
1023 	UBool result = co->ucal->equals(*other_co->ucal, CALENDAR_ERROR_CODE(co));
1024 	INTL_METHOD_CHECK_STATUS(co, "intlcal_equals: error calling ICU Calendar::equals");
1025 
1026 	RETURN_BOOL((int)result);
1027 }
1028 
PHP_FUNCTION(intlcal_get_repeated_wall_time_option)1029 U_CFUNC PHP_FUNCTION(intlcal_get_repeated_wall_time_option)
1030 {
1031 	CALENDAR_METHOD_INIT_VARS;
1032 
1033 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1034 			"O", &object, Calendar_ce_ptr) == FAILURE) {
1035 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1036 			"intlcal_get_repeated_wall_time_option: bad arguments", 0);
1037 		RETURN_FALSE;
1038 	}
1039 
1040 	CALENDAR_METHOD_FETCH_OBJECT;
1041 
1042 	RETURN_LONG(co->ucal->getRepeatedWallTimeOption());
1043 }
1044 
PHP_FUNCTION(intlcal_get_skipped_wall_time_option)1045 U_CFUNC PHP_FUNCTION(intlcal_get_skipped_wall_time_option)
1046 {
1047 	CALENDAR_METHOD_INIT_VARS;
1048 
1049 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1050 			"O", &object, Calendar_ce_ptr) == FAILURE) {
1051 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1052 			"intlcal_get_skipped_wall_time_option: bad arguments", 0);
1053 		RETURN_FALSE;
1054 	}
1055 
1056 	CALENDAR_METHOD_FETCH_OBJECT;
1057 
1058 	RETURN_LONG(co->ucal->getSkippedWallTimeOption());
1059 }
1060 
PHP_FUNCTION(intlcal_set_repeated_wall_time_option)1061 U_CFUNC PHP_FUNCTION(intlcal_set_repeated_wall_time_option)
1062 {
1063 	zend_long	option;
1064 	CALENDAR_METHOD_INIT_VARS;
1065 
1066 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1067 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1068 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1069 			"intlcal_set_repeated_wall_time_option: bad arguments", 0);
1070 		RETURN_FALSE;
1071 	}
1072 
1073 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST) {
1074 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1075 			"intlcal_set_repeated_wall_time_option: invalid option", 0);
1076 		RETURN_FALSE;
1077 	}
1078 
1079 	CALENDAR_METHOD_FETCH_OBJECT;
1080 
1081 	co->ucal->setRepeatedWallTimeOption((UCalendarWallTimeOption)option);
1082 
1083 	RETURN_TRUE;
1084 }
1085 
PHP_FUNCTION(intlcal_set_skipped_wall_time_option)1086 U_CFUNC PHP_FUNCTION(intlcal_set_skipped_wall_time_option)
1087 {
1088 	zend_long	option;
1089 	CALENDAR_METHOD_INIT_VARS;
1090 
1091 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(),
1092 			"Ol", &object, Calendar_ce_ptr, &option) == FAILURE) {
1093 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1094 			"intlcal_set_skipped_wall_time_option: bad arguments", 0);
1095 		RETURN_FALSE;
1096 	}
1097 
1098 	if (option != UCAL_WALLTIME_FIRST && option != UCAL_WALLTIME_LAST
1099 			&& option != UCAL_WALLTIME_NEXT_VALID) {
1100 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1101 			"intlcal_set_skipped_wall_time_option: invalid option", 0);
1102 		RETURN_FALSE;
1103 	}
1104 
1105 	CALENDAR_METHOD_FETCH_OBJECT;
1106 
1107 	co->ucal->setSkippedWallTimeOption((UCalendarWallTimeOption)option);
1108 
1109 	RETURN_TRUE;
1110 }
1111 
PHP_FUNCTION(intlcal_from_date_time)1112 U_CFUNC PHP_FUNCTION(intlcal_from_date_time)
1113 {
1114 	zval			*zv_arg,
1115 					zv_tmp,
1116 					*zv_datetime  		= NULL,
1117 					zv_timestamp;
1118 	php_date_obj	*datetime;
1119 	char			*locale_str			= NULL;
1120 	size_t				locale_str_len;
1121 	TimeZone		*timeZone;
1122 	UErrorCode		status				= U_ZERO_ERROR;
1123 	Calendar        *cal;
1124 	intl_error_reset(NULL);
1125 
1126 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!",
1127 			&zv_arg, &locale_str, &locale_str_len) == FAILURE) {
1128 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1129 			"intlcal_from_date_time: bad arguments", 0);
1130 		RETURN_NULL();
1131 	}
1132 
1133 	if (!(Z_TYPE_P(zv_arg) == IS_OBJECT && instanceof_function(
1134 			Z_OBJCE_P(zv_arg), php_date_get_date_ce()))) {
1135 		object_init_ex(&zv_tmp, php_date_get_date_ce());
1136 		zend_call_method_with_1_params(&zv_tmp, NULL, &Z_OBJCE(zv_tmp)->constructor, "__construct", NULL, zv_arg);
1137 		zv_datetime = &zv_tmp;
1138 		if (EG(exception)) {
1139 			zend_object_store_ctor_failed(Z_OBJ(zv_tmp));
1140 			goto error;
1141 		}
1142 	} else {
1143 		zv_datetime = zv_arg;
1144 	}
1145 
1146 	datetime = Z_PHPDATE_P(zv_datetime);
1147 	if (!datetime->time) {
1148 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1149 			"intlcal_from_date_time: DateTime object is unconstructed",
1150 			0);
1151 		goto error;
1152 	}
1153 
1154 	zend_call_method_with_0_params(zv_datetime, php_date_get_date_ce(), NULL, "gettimestamp", &zv_timestamp);
1155 	if (Z_TYPE(zv_timestamp) != IS_LONG) {
1156 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1157 			"intlcal_from_date_time: bad DateTime; call to "
1158 			"DateTime::getTimestamp() failed", 0);
1159 		zval_ptr_dtor(&zv_timestamp);
1160 		goto error;
1161 	}
1162 
1163 	if (!datetime->time->is_localtime) {
1164 		timeZone = TimeZone::getGMT()->clone();
1165 	} else {
1166 		timeZone = timezone_convert_datetimezone(datetime->time->zone_type,
1167 			datetime, 1, NULL, "intlcal_from_date_time");
1168 		if (timeZone == NULL) {
1169 			goto error;
1170 		}
1171 	}
1172 
1173 	if (!locale_str) {
1174 		locale_str = const_cast<char*>(intl_locale_get_default());
1175 	}
1176 
1177 	cal = Calendar::createInstance(timeZone,
1178 		Locale::createFromName(locale_str), status);
1179 	if (cal == NULL) {
1180 		delete timeZone;
1181 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1182 				"error creating ICU Calendar object", 0);
1183 		goto error;
1184 	}
1185 	cal->setTime(((UDate)Z_LVAL(zv_timestamp)) * 1000., status);
1186     if (U_FAILURE(status)) {
1187 		/* time zone was adopted by cal; should not be deleted here */
1188 		delete cal;
1189 		intl_error_set(NULL, status, "intlcal_from_date_time: "
1190 				"error creating ICU Calendar::setTime()", 0);
1191         goto error;
1192     }
1193 
1194 	calendar_object_create(return_value, cal);
1195 
1196 error:
1197 	if (zv_datetime && zv_datetime != zv_arg) {
1198 		zval_ptr_dtor(zv_datetime);
1199 	}
1200 }
1201 
PHP_FUNCTION(intlcal_to_date_time)1202 U_CFUNC PHP_FUNCTION(intlcal_to_date_time)
1203 {
1204 	zval retval;
1205 	CALENDAR_METHOD_INIT_VARS;
1206 
1207 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1208 			&object, Calendar_ce_ptr) == FAILURE) {
1209 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1210 			"intlcal_to_date_time: bad arguments", 0);
1211 		RETURN_FALSE;
1212 	}
1213 
1214 	CALENDAR_METHOD_FETCH_OBJECT;
1215 
1216 	/* There are no exported functions in ext/date to this
1217 	 * in a more native fashion */
1218 	double	date = co->ucal->getTime(CALENDAR_ERROR_CODE(co)) / 1000.;
1219 	int64_t	ts;
1220 	char	ts_str[sizeof("@-9223372036854775808")];
1221 	int		ts_str_len;
1222 	zval	ts_zval, tmp;
1223 
1224 	INTL_METHOD_CHECK_STATUS(co, "Call to ICU method has failed");
1225 
1226 	if (date > (double)U_INT64_MAX || date < (double)U_INT64_MIN) {
1227 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1228 			"intlcal_to_date_time: The calendar date is out of the "
1229 			"range for a 64-bit integer", 0);
1230 		RETURN_FALSE;
1231 	}
1232 
1233 	ZVAL_UNDEF(&retval);
1234 	ts = (int64_t)date;
1235 
1236 	ts_str_len = slprintf(ts_str, sizeof(ts_str), "@%" PRIi64, ts);
1237 	ZVAL_STRINGL(&ts_zval, ts_str, ts_str_len);
1238 
1239 	/* Now get the time zone */
1240 	const TimeZone& tz = co->ucal->getTimeZone();
1241 	zval *timezone_zval = timezone_convert_to_datetimezone(
1242 		&tz, CALENDAR_ERROR_P(co), "intlcal_to_date_time", &tmp);
1243 	if (timezone_zval == NULL) {
1244 		zval_ptr_dtor(&ts_zval);
1245 		RETURN_FALSE;
1246 	}
1247 
1248 	/* resources allocated from now on */
1249 
1250 	/* Finally, instantiate object and call constructor */
1251 	object_init_ex(return_value, php_date_get_date_ce());
1252 	zend_call_method_with_2_params(return_value, NULL, &Z_OBJCE_P(return_value)->constructor, "__construct", NULL, &ts_zval, timezone_zval);
1253 	if (EG(exception)) {
1254 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1255 			"intlcal_to_date_time: DateTime constructor has thrown exception",
1256 			1);
1257 		zend_object_store_ctor_failed(Z_OBJ_P(return_value));
1258 		zval_ptr_dtor(return_value);
1259 		zval_ptr_dtor(&ts_zval);
1260 
1261 		RETVAL_FALSE;
1262 		goto error;
1263 	}
1264 	zval_ptr_dtor(&ts_zval);
1265 
1266 	/* due to bug #40743, we have to set the time zone again */
1267 	zend_call_method_with_1_params(return_value, NULL, NULL, "settimezone",
1268 			&retval, timezone_zval);
1269 	if (Z_ISUNDEF(retval) || Z_TYPE(retval) == IS_FALSE) {
1270 		intl_errors_set(CALENDAR_ERROR_P(co), U_ILLEGAL_ARGUMENT_ERROR,
1271 			"intlcal_to_date_time: call to DateTime::setTimeZone has failed",
1272 			1);
1273 		zval_ptr_dtor(return_value);
1274 		RETVAL_FALSE;
1275 		goto error;
1276 	}
1277 
1278 error:
1279 	zval_ptr_dtor(timezone_zval);
1280 	zval_ptr_dtor(&retval);
1281 }
1282 
PHP_FUNCTION(intlcal_get_error_code)1283 U_CFUNC PHP_FUNCTION(intlcal_get_error_code)
1284 {
1285 	CALENDAR_METHOD_INIT_VARS;
1286 
1287 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1288 			&object, Calendar_ce_ptr) == FAILURE) {
1289 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
1290 			"intlcal_get_error_code: bad arguments", 0);
1291 		RETURN_FALSE;
1292 	}
1293 
1294 	/* Fetch the object (without resetting its last error code ). */
1295 	co = Z_INTL_CALENDAR_P(object);
1296 	if (co == NULL)
1297 		RETURN_FALSE;
1298 
1299 	RETURN_LONG((zend_long)CALENDAR_ERROR_CODE(co));
1300 }
1301 
PHP_FUNCTION(intlcal_get_error_message)1302 U_CFUNC PHP_FUNCTION(intlcal_get_error_message)
1303 {
1304 	zend_string* message = NULL;
1305 	CALENDAR_METHOD_INIT_VARS;
1306 
1307 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
1308 			&object, Calendar_ce_ptr) == FAILURE) {
1309 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
1310 			"intlcal_get_error_message: bad arguments", 0 );
1311 		RETURN_FALSE;
1312 	}
1313 
1314 
1315 	/* Fetch the object (without resetting its last error code ). */
1316 	co = Z_INTL_CALENDAR_P(object);
1317 	if (co == NULL)
1318 		RETURN_FALSE;
1319 
1320 	/* Return last error message. */
1321 	message = intl_error_get_message(CALENDAR_ERROR_P(co));
1322 	RETURN_STR(message);
1323 }
1324