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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
14    |          Gustavo Lopes <cataphract@php.net>                          |
15    +----------------------------------------------------------------------+
16 */
17 
18 #include "../intl_cppshims.h"
19 
20 #include <unicode/timezone.h>
21 #include <unicode/calendar.h>
22 #include <unicode/datefmt.h>
23 
24 extern "C" {
25 #include <unicode/ustring.h>
26 #include <unicode/udat.h>
27 
28 #include "php_intl.h"
29 #include "dateformat_create.h"
30 #include "dateformat_class.h"
31 #define USE_TIMEZONE_POINTER 1
32 #include "../timezone/timezone_class.h"
33 #include "../intl_convert.h"
34 }
35 
36 #include "dateformat_helpers.h"
37 #include "zend_exceptions.h"
38 
39 #if U_ICU_VERSION_MAJOR_NUM < 50
40 #define UDAT_PATTERN 0
41 #endif
42 
43 #define INTL_UDATE_FMT_OK(i) \
44 	(UDAT_FULL == (i) || UDAT_LONG == (i) ||    \
45 	 UDAT_MEDIUM == (i) || UDAT_SHORT == (i) || \
46 	 UDAT_RELATIVE == (i) || UDAT_FULL_RELATIVE == (i) || \
47 	 UDAT_LONG_RELATIVE == (i) || UDAT_MEDIUM_RELATIVE == (i) || \
48 	 UDAT_SHORT_RELATIVE == (i) || UDAT_NONE == (i) || \
49 	 UDAT_PATTERN == (i))
50 
51 /* {{{ */
datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS,zend_bool is_constructor)52 static int datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_constructor)
53 {
54 	zval		*object;
55 	const char	*locale_str;
56 	size_t		locale_len	= 0;
57 	Locale		locale;
58 	zend_long	date_type	= 0;
59 	zend_long	time_type	= 0;
60 	zval		*calendar_zv	= NULL;
61 	Calendar	*calendar	= NULL;
62 	zend_long	calendar_type;
63 	bool		calendar_owned;
64 	zval		*timezone_zv	= NULL;
65 	TimeZone	*timezone	= NULL;
66 	bool		explicit_tz;
67 	char*       pattern_str		= NULL;
68 	size_t      pattern_str_len	= 0;
69 	UChar*      svalue		= NULL;		/* UTF-16 pattern_str */
70 	int32_t     slength		= 0;
71 	IntlDateFormatter_object* dfo;
72 	int zpp_flags = is_constructor ? ZEND_PARSE_PARAMS_THROW : 0;
73 
74 	intl_error_reset(NULL);
75 	object = return_value;
76 	/* Parse parameters. */
77 	if (zend_parse_parameters_ex(zpp_flags, ZEND_NUM_ARGS(), "s!ll|zzs",
78 			&locale_str, &locale_len, &date_type, &time_type, &timezone_zv,
79 			&calendar_zv, &pattern_str, &pattern_str_len) == FAILURE) {
80 		intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,	"datefmt_create: "
81 				"unable to parse input parameters", 0);
82 		return FAILURE;
83 	}
84 
85 	DATE_FORMAT_METHOD_FETCH_OBJECT_NO_CHECK;
86 
87 	if (DATE_FORMAT_OBJECT(dfo) != NULL) {
88 		intl_errors_set(INTL_DATA_ERROR_P(dfo), U_ILLEGAL_ARGUMENT_ERROR,
89 				"datefmt_create: cannot call constructor twice", 0);
90 		return FAILURE;
91 	}
92 
93 	if (!INTL_UDATE_FMT_OK(date_type)) {
94 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: invalid date format style", 0);
95 		return FAILURE;
96 	}
97 	if (!INTL_UDATE_FMT_OK(time_type)) {
98 		intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: invalid time format style", 0);
99 		return FAILURE;
100 	}
101 
102 	INTL_CHECK_LOCALE_LEN_OR_FAILURE(locale_len);
103 	if (locale_len == 0) {
104 		locale_str = intl_locale_get_default();
105 	}
106 	locale = Locale::createFromName(locale_str);
107 
108 	/* process calendar */
109 	if (datefmt_process_calendar_arg(calendar_zv, locale, "datefmt_create",
110 			INTL_DATA_ERROR_P(dfo), calendar, calendar_type,
111 			calendar_owned)
112 			== FAILURE) {
113 		goto error;
114 	}
115 
116 	/* process timezone */
117 	explicit_tz = timezone_zv != NULL && Z_TYPE_P(timezone_zv) != IS_NULL;
118 
119 	if (explicit_tz || calendar_owned ) {
120 		//we have an explicit time zone or a non-object calendar
121 		timezone = timezone_process_timezone_argument(timezone_zv,
122 				INTL_DATA_ERROR_P(dfo), "datefmt_create");
123 		if (timezone == NULL) {
124 			goto error;
125 		}
126 	}
127 
128 	/* Convert pattern (if specified) to UTF-16. */
129 	if (pattern_str && pattern_str_len > 0) {
130 		intl_convert_utf8_to_utf16(&svalue, &slength,
131 				pattern_str, pattern_str_len, &INTL_DATA_ERROR_CODE(dfo));
132 		if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
133 			/* object construction -> only set global error */
134 			intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: "
135 					"error converting pattern to UTF-16", 0);
136 			goto error;
137 		}
138 	}
139 
140 	DATE_FORMAT_OBJECT(dfo) = udat_open((UDateFormatStyle)time_type,
141 			(UDateFormatStyle)date_type, locale_str, NULL, 0, svalue,
142 			slength, &INTL_DATA_ERROR_CODE(dfo));
143 
144 	if (pattern_str && pattern_str_len > 0) {
145 		udat_applyPattern(DATE_FORMAT_OBJECT(dfo), true, svalue, slength);
146 		if (U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
147 			intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo), "datefmt_create: error applying pattern", 0);
148 			goto error;
149 		}
150 	}
151 
152 	if (!U_FAILURE(INTL_DATA_ERROR_CODE(dfo))) {
153 		DateFormat *df = (DateFormat*)DATE_FORMAT_OBJECT(dfo);
154 		if (calendar_owned) {
155 			df->adoptCalendar(calendar);
156 			calendar_owned = false;
157 		} else {
158 			df->setCalendar(*calendar);
159 		}
160 
161 		if (timezone != NULL) {
162 			df->adoptTimeZone(timezone);
163 		}
164 	} else {
165 		intl_error_set(NULL, INTL_DATA_ERROR_CODE(dfo),	"datefmt_create: date "
166 				"formatter creation failed", 0);
167 		goto error;
168 	}
169 
170 	/* Set the class variables */
171 	dfo->date_type			= date_type;
172 	dfo->time_type			= time_type;
173 	dfo->calendar			= calendar_type;
174 	dfo->requested_locale	= estrdup(locale_str);
175 
176 error:
177 	if (svalue) {
178 		efree(svalue);
179 	}
180 	if (timezone != NULL && DATE_FORMAT_OBJECT(dfo) == NULL) {
181 		delete timezone;
182 	}
183 	if (calendar != NULL && calendar_owned) {
184 		delete calendar;
185 	}
186 
187 	return U_FAILURE(intl_error_get_code(NULL)) ? FAILURE : SUCCESS;
188 }
189 /* }}} */
190 
191 /* {{{ proto IntlDateFormatter IntlDateFormatter::create(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern] )
192  * Create formatter. }}} */
193 /* {{{ proto IntlDateFormatter datefmt_create(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern)
194  * Create formatter.
195  */
PHP_FUNCTION(datefmt_create)196 U_CFUNC PHP_FUNCTION( datefmt_create )
197 {
198     object_init_ex( return_value, IntlDateFormatter_ce_ptr );
199 	if (datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0) == FAILURE) {
200 		zval_ptr_dtor(return_value);
201 		RETURN_NULL();
202 	}
203 }
204 /* }}} */
205 
206 /* {{{ proto void IntlDateFormatter::__construct(string $locale, long date_type, long time_type[, string $timezone_str, long $calendar, string $pattern])
207  * IntlDateFormatter object constructor.
208  */
PHP_METHOD(IntlDateFormatter,__construct)209 U_CFUNC PHP_METHOD( IntlDateFormatter, __construct )
210 {
211 	zend_error_handling error_handling;
212 
213 	zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);
214 	/* return_value param is being changed, therefore we will always return
215 	 * NULL here */
216 	return_value = getThis();
217 	if (datefmt_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1) == FAILURE) {
218 		if (!EG(exception)) {
219 			zend_string *err = intl_error_get_message(NULL);
220 			zend_throw_exception(IntlException_ce_ptr, ZSTR_VAL(err), intl_error_get_code(NULL));
221 			zend_string_release_ex(err, 0);
222 		}
223 	}
224 	zend_restore_error_handling(&error_handling);
225 }
226 /* }}} */
227