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