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