1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
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 | https://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: Derick Rethans <derick@derickrethans.nl> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18 #include "php_streams.h"
19 #include "php_main.h"
20 #include "php_globals.h"
21 #include "php_ini.h"
22 #include "ext/standard/info.h"
23 #include "ext/standard/php_versioning.h"
24 #include "ext/standard/php_math.h"
25 #include "php_date.h"
26 #include "zend_interfaces.h"
27 #include "zend_exceptions.h"
28 #include "lib/timelib.h"
29 #include "lib/timelib_private.h"
30 #ifndef PHP_WIN32
31 #include <time.h>
32 #else
33 #include "win32/time.h"
34 #endif
35
36 #ifdef PHP_WIN32
php_date_llabs(__int64 i)37 static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; }
38 #elif defined(__GNUC__) && __GNUC__ < 3
php_date_llabs(__int64_t i)39 static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; }
40 #else
php_date_llabs(long long i)41 static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; }
42 #endif
43
44 #ifdef PHP_WIN32
45 #define DATE_I64_BUF_LEN 65
46 # define DATE_I64A(i, s, len) _i64toa_s(i, s, len, 10)
47 # define DATE_A64I(i, s) i = _atoi64(s)
48 #else
49 #define DATE_I64_BUF_LEN 65
50 # define DATE_I64A(i, s, len) \
51 do { \
52 int st = snprintf(s, len, "%lld", i); \
53 s[st] = '\0'; \
54 } while (0);
55 #define DATE_A64I(i, s) i = strtoll(s, NULL, 10)
56 #endif
57
php_time(void)58 PHPAPI time_t php_time(void)
59 {
60 #ifdef HAVE_GETTIMEOFDAY
61 struct timeval tm;
62
63 if (UNEXPECTED(gettimeofday(&tm, NULL) != SUCCESS)) {
64 /* fallback, can't reasonably happen */
65 return time(NULL);
66 }
67
68 return tm.tv_sec;
69 #else
70 return time(NULL);
71 #endif
72 }
73
74 /*
75 * RFC822, Section 5.1: http://www.ietf.org/rfc/rfc822.txt
76 * date-time = [ day "," ] date time ; dd mm yy hh:mm:ss zzz
77 * day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
78 * date = 1*2DIGIT month 2DIGIT ; day month year e.g. 20 Jun 82
79 * month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
80 * time = hour zone ; ANSI and Military
81 * hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59
82 * zone = "UT" / "GMT" / "EST" / "EDT" / "CST" / "CDT" / "MST" / "MDT" / "PST" / "PDT" / 1ALPHA / ( ("+" / "-") 4DIGIT )
83 */
84 #define DATE_FORMAT_RFC822 "D, d M y H:i:s O"
85
86 /*
87 * RFC850, Section 2.1.4: http://www.ietf.org/rfc/rfc850.txt
88 * Format must be acceptable both to the ARPANET and to the getdate routine.
89 * One format that is acceptable to both is Weekday, DD-Mon-YY HH:MM:SS TIMEZONE
90 * TIMEZONE can be any timezone name (3 or more letters)
91 */
92 #define DATE_FORMAT_RFC850 "l, d-M-y H:i:s T"
93
94 /*
95 * RFC1036, Section 2.1.2: http://www.ietf.org/rfc/rfc1036.txt
96 * Its format must be acceptable both in RFC-822 and to the getdate(3)
97 * Wdy, DD Mon YY HH:MM:SS TIMEZONE
98 * There is no hope of having a complete list of timezones. Universal
99 * Time (GMT), the North American timezones (PST, PDT, MST, MDT, CST,
100 * CDT, EST, EDT) and the +/-hhmm offset specified in RFC-822 should be supported.
101 */
102 #define DATE_FORMAT_RFC1036 "D, d M y H:i:s O"
103
104 /*
105 * RFC1123, Section 5.2.14: http://www.ietf.org/rfc/rfc1123.txt
106 * RFC-822 Date and Time Specification: RFC-822 Section 5
107 * The syntax for the date is hereby changed to: date = 1*2DIGIT month 2*4DIGIT
108 */
109 #define DATE_FORMAT_RFC1123 "D, d M Y H:i:s O"
110
111 /*
112 * RFC7231, Section 7.1.1: http://tools.ietf.org/html/rfc7231
113 */
114 #define DATE_FORMAT_RFC7231 "D, d M Y H:i:s \\G\\M\\T"
115
116 /*
117 * RFC2822, Section 3.3: http://www.ietf.org/rfc/rfc2822.txt
118 * FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space
119 * CFWS = *([FWS] comment) (([FWS] comment) / FWS)
120 *
121 * date-time = [ day-of-week "," ] date FWS time [CFWS]
122 * day-of-week = ([FWS] day-name)
123 * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
124 * date = day month year
125 * year = 4*DIGIT
126 * month = (FWS month-name FWS)
127 * month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
128 * day = ([FWS] 1*2DIGIT)
129 * time = time-of-day FWS zone
130 * time-of-day = hour ":" minute [ ":" second ]
131 * hour = 2DIGIT
132 * minute = 2DIGIT
133 * second = 2DIGIT
134 * zone = (( "+" / "-" ) 4DIGIT)
135 */
136 #define DATE_FORMAT_RFC2822 "D, d M Y H:i:s O"
137
138 /*
139 * RFC3339, Section 5.6: http://www.ietf.org/rfc/rfc3339.txt
140 * date-fullyear = 4DIGIT
141 * date-month = 2DIGIT ; 01-12
142 * date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
143 *
144 * time-hour = 2DIGIT ; 00-23
145 * time-minute = 2DIGIT ; 00-59
146 * time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
147 *
148 * time-secfrac = "." 1*DIGIT
149 * time-numoffset = ("+" / "-") time-hour ":" time-minute
150 * time-offset = "Z" / time-numoffset
151 *
152 * partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
153 * full-date = date-fullyear "-" date-month "-" date-mday
154 * full-time = partial-time time-offset
155 *
156 * date-time = full-date "T" full-time
157 */
158 #define DATE_FORMAT_RFC3339 "Y-m-d\\TH:i:sP"
159
160 /*
161 * This format does not technically match the ISO 8601 standard, as it does not
162 * use : in the UTC offset format specifier. This is kept for BC reasons. The
163 * DATE_FORMAT_ISO8601_EXPANDED format does correct this, as well as adding
164 * support for years out side of the traditional 0000-9999 range.
165 */
166 #define DATE_FORMAT_ISO8601 "Y-m-d\\TH:i:sO"
167
168 /* ISO 8601:2004(E)
169 *
170 * Section 3.5 Expansion:
171 * By mutual agreement of the partners in information interchange, it is
172 * permitted to expand the component identifying the calendar year, which is
173 * otherwise limited to four digits. This enables reference to dates and times
174 * in calendar years outside the range supported by complete representations,
175 * i.e. before the start of the year [0000] or after the end of the year
176 * [9999]."
177 *
178 * Section 4.1.2.4 Expanded representations:
179 * If, by agreement, expanded representations are used, the formats shall be as
180 * specified below. The interchange parties shall agree the additional number of
181 * digits in the time element year. In the examples below it has been agreed to
182 * expand the time element year with two digits.
183 * Extended format: ±YYYYY-MM-DD
184 * Example: +001985-04-12
185 *
186 * PHP's year expansion digits are variable.
187 */
188 #define DATE_FORMAT_ISO8601_EXPANDED "X-m-d\\TH:i:sP"
189
190 /* Internal Only
191 * This format only extends the year when needed, keeping the 'P' format with
192 * colon for UTC offsets
193 */
194 #define DATE_FORMAT_ISO8601_LARGE_YEAR "x-m-d\\TH:i:sP"
195
196 /*
197 * RFC3339, Appendix A: http://www.ietf.org/rfc/rfc3339.txt
198 * ISO 8601 also requires (in section 5.3.1.3) that a decimal fraction
199 * be proceeded by a "0" if less than unity. Annex B.2 of ISO 8601
200 * gives examples where the decimal fractions are not preceded by a "0".
201 * This grammar assumes section 5.3.1.3 is correct and that Annex B.2 is
202 * in error.
203 */
204 #define DATE_FORMAT_RFC3339_EXTENDED "Y-m-d\\TH:i:s.vP"
205
206 /*
207 * This comes from various sources that like to contradict. I'm going with the
208 * format here because of:
209 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa384321%28v=vs.85%29.aspx
210 * and http://curl.haxx.se/rfc/cookie_spec.html
211 */
212 #define DATE_FORMAT_COOKIE "l, d-M-Y H:i:s T"
213
214 #define SUNFUNCS_RET_TIMESTAMP 0
215 #define SUNFUNCS_RET_STRING 1
216 #define SUNFUNCS_RET_DOUBLE 2
217
218 #define PHP_DATE_TIMEZONE_GROUP_AFRICA 0x0001
219 #define PHP_DATE_TIMEZONE_GROUP_AMERICA 0x0002
220 #define PHP_DATE_TIMEZONE_GROUP_ANTARCTICA 0x0004
221 #define PHP_DATE_TIMEZONE_GROUP_ARCTIC 0x0008
222 #define PHP_DATE_TIMEZONE_GROUP_ASIA 0x0010
223 #define PHP_DATE_TIMEZONE_GROUP_ATLANTIC 0x0020
224 #define PHP_DATE_TIMEZONE_GROUP_AUSTRALIA 0x0040
225 #define PHP_DATE_TIMEZONE_GROUP_EUROPE 0x0080
226 #define PHP_DATE_TIMEZONE_GROUP_INDIAN 0x0100
227 #define PHP_DATE_TIMEZONE_GROUP_PACIFIC 0x0200
228 #define PHP_DATE_TIMEZONE_GROUP_UTC 0x0400
229 #define PHP_DATE_TIMEZONE_GROUP_ALL 0x07FF
230 #define PHP_DATE_TIMEZONE_GROUP_ALL_W_BC 0x0FFF
231 #define PHP_DATE_TIMEZONE_PER_COUNTRY 0x1000
232
233 #define PHP_DATE_PERIOD_EXCLUDE_START_DATE 0x0001
234 #define PHP_DATE_PERIOD_INCLUDE_END_DATE 0x0002
235
236 #include "php_date_arginfo.h"
237
238 static char* guess_timezone(const timelib_tzdb *tzdb);
239 static void date_register_classes(void);
240 /* }}} */
241
242 ZEND_DECLARE_MODULE_GLOBALS(date)
243 static PHP_GINIT_FUNCTION(date);
244
245 /* True global */
246 timelib_tzdb *php_date_global_timezone_db;
247 int php_date_global_timezone_db_enabled;
248
249 #define DATE_DEFAULT_LATITUDE "31.7667"
250 #define DATE_DEFAULT_LONGITUDE "35.2333"
251
252 /* on 90'50; common sunset declaration (start of sun body appear) */
253 #define DATE_SUNSET_ZENITH "90.833333"
254
255 /* on 90'50; common sunrise declaration (sun body disappeared) */
256 #define DATE_SUNRISE_ZENITH "90.833333"
257
258 static PHP_INI_MH(OnUpdate_date_timezone);
259
260 /* {{{ INI Settings */
261 PHP_INI_BEGIN()
262 STD_PHP_INI_ENTRY("date.timezone", "UTC", PHP_INI_ALL, OnUpdate_date_timezone, default_timezone, zend_date_globals, date_globals)
263 PHP_INI_ENTRY("date.default_latitude", DATE_DEFAULT_LATITUDE, PHP_INI_ALL, NULL)
264 PHP_INI_ENTRY("date.default_longitude", DATE_DEFAULT_LONGITUDE, PHP_INI_ALL, NULL)
265 PHP_INI_ENTRY("date.sunset_zenith", DATE_SUNSET_ZENITH, PHP_INI_ALL, NULL)
266 PHP_INI_ENTRY("date.sunrise_zenith", DATE_SUNRISE_ZENITH, PHP_INI_ALL, NULL)
267 PHP_INI_END()
268 /* }}} */
269
270 zend_class_entry *date_ce_date, *date_ce_timezone, *date_ce_interval, *date_ce_period;
271 zend_class_entry *date_ce_immutable, *date_ce_interface;
272
273
php_date_get_date_ce(void)274 PHPAPI zend_class_entry *php_date_get_date_ce(void)
275 {
276 return date_ce_date;
277 }
278
php_date_get_immutable_ce(void)279 PHPAPI zend_class_entry *php_date_get_immutable_ce(void)
280 {
281 return date_ce_immutable;
282 }
283
php_date_get_interface_ce(void)284 PHPAPI zend_class_entry *php_date_get_interface_ce(void)
285 {
286 return date_ce_interface;
287 }
288
php_date_get_timezone_ce(void)289 PHPAPI zend_class_entry *php_date_get_timezone_ce(void)
290 {
291 return date_ce_timezone;
292 }
293
php_date_get_interval_ce(void)294 PHPAPI zend_class_entry *php_date_get_interval_ce(void)
295 {
296 return date_ce_interval;
297 }
298
php_date_get_period_ce(void)299 PHPAPI zend_class_entry *php_date_get_period_ce(void)
300 {
301 return date_ce_period;
302 }
303
304 static zend_object_handlers date_object_handlers_date;
305 static zend_object_handlers date_object_handlers_immutable;
306 static zend_object_handlers date_object_handlers_timezone;
307 static zend_object_handlers date_object_handlers_interval;
308 static zend_object_handlers date_object_handlers_period;
309
310 #define DATE_CHECK_INITIALIZED(member, class_name) \
311 if (!(member)) { \
312 zend_throw_error(NULL, "The " #class_name " object has not been correctly initialized by its constructor"); \
313 RETURN_THROWS(); \
314 }
315
316 static void date_object_free_storage_date(zend_object *object);
317 static void date_object_free_storage_timezone(zend_object *object);
318 static void date_object_free_storage_interval(zend_object *object);
319 static void date_object_free_storage_period(zend_object *object);
320
321 static zend_object *date_object_new_date(zend_class_entry *class_type);
322 static zend_object *date_object_new_timezone(zend_class_entry *class_type);
323 static zend_object *date_object_new_interval(zend_class_entry *class_type);
324 static zend_object *date_object_new_period(zend_class_entry *class_type);
325
326 static zend_object *date_object_clone_date(zend_object *this_ptr);
327 static zend_object *date_object_clone_timezone(zend_object *this_ptr);
328 static zend_object *date_object_clone_interval(zend_object *this_ptr);
329 static zend_object *date_object_clone_period(zend_object *this_ptr);
330
331 static int date_object_compare_date(zval *d1, zval *d2);
332 static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n);
333 static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose);
334 static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n);
335 static HashTable *date_object_get_properties_interval(zend_object *object);
336 static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n);
337 static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose);
338 static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n);
339 static HashTable *date_object_get_debug_info_timezone(zend_object *object, int *is_temp);
340 static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv);
341
342 static int date_interval_compare_objects(zval *o1, zval *o2);
343 static zval *date_interval_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv);
344 static zval *date_interval_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot);
345 static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot);
346 static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv);
347 static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot);
348 static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot);
349
350 static int date_object_compare_timezone(zval *tz1, zval *tz2);
351
352 /* {{{ Module struct */
353 zend_module_entry date_module_entry = {
354 STANDARD_MODULE_HEADER_EX,
355 NULL,
356 NULL,
357 "date", /* extension name */
358 ext_functions, /* function list */
359 PHP_MINIT(date), /* process startup */
360 PHP_MSHUTDOWN(date), /* process shutdown */
361 PHP_RINIT(date), /* request startup */
362 PHP_RSHUTDOWN(date), /* request shutdown */
363 PHP_MINFO(date), /* extension info */
364 PHP_DATE_VERSION, /* extension version */
365 PHP_MODULE_GLOBALS(date), /* globals descriptor */
366 PHP_GINIT(date), /* globals ctor */
367 NULL, /* globals dtor */
368 ZEND_MODULE_POST_ZEND_DEACTIVATE_N(date), /* post deactivate */
369 STANDARD_MODULE_PROPERTIES_EX
370 };
371 /* }}} */
372
373
374 /* {{{ PHP_GINIT_FUNCTION */
PHP_GINIT_FUNCTION(date)375 static PHP_GINIT_FUNCTION(date)
376 {
377 date_globals->default_timezone = NULL;
378 date_globals->timezone = NULL;
379 date_globals->tzcache = NULL;
380 }
381 /* }}} */
382
383
_php_date_tzinfo_dtor(zval * zv)384 static void _php_date_tzinfo_dtor(zval *zv) /* {{{ */
385 {
386 timelib_tzinfo *tzi = (timelib_tzinfo*)Z_PTR_P(zv);
387
388 timelib_tzinfo_dtor(tzi);
389 } /* }}} */
390
391 /* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(date)392 PHP_RINIT_FUNCTION(date)
393 {
394 if (DATEG(timezone)) {
395 efree(DATEG(timezone));
396 }
397 DATEG(timezone) = NULL;
398 DATEG(tzcache) = NULL;
399 DATEG(last_errors) = NULL;
400
401 return SUCCESS;
402 }
403 /* }}} */
404
405 /* {{{ PHP_RSHUTDOWN_FUNCTION */
PHP_RSHUTDOWN_FUNCTION(date)406 PHP_RSHUTDOWN_FUNCTION(date)
407 {
408 if (DATEG(timezone)) {
409 efree(DATEG(timezone));
410 }
411 DATEG(timezone) = NULL;
412
413 return SUCCESS;
414 }
415 /* }}} */
416
ZEND_MODULE_POST_ZEND_DEACTIVATE_D(date)417 ZEND_MODULE_POST_ZEND_DEACTIVATE_D(date)
418 {
419 if (DATEG(tzcache)) {
420 zend_hash_destroy(DATEG(tzcache));
421 FREE_HASHTABLE(DATEG(tzcache));
422 DATEG(tzcache) = NULL;
423 }
424
425 if (DATEG(last_errors)) {
426 timelib_error_container_dtor(DATEG(last_errors));
427 DATEG(last_errors) = NULL;
428 }
429
430 return SUCCESS;
431 }
432
433 #define DATE_TIMEZONEDB php_date_global_timezone_db ? php_date_global_timezone_db : timelib_builtin_db()
434
435 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(date)436 PHP_MINIT_FUNCTION(date)
437 {
438 REGISTER_INI_ENTRIES();
439 date_register_classes();
440 register_php_date_symbols(module_number);
441
442 php_date_global_timezone_db = NULL;
443 php_date_global_timezone_db_enabled = 0;
444 DATEG(last_errors) = NULL;
445 return SUCCESS;
446 }
447 /* }}} */
448
449 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(date)450 PHP_MSHUTDOWN_FUNCTION(date)
451 {
452 UNREGISTER_INI_ENTRIES();
453
454 if (DATEG(last_errors)) {
455 timelib_error_container_dtor(DATEG(last_errors));
456 }
457
458 #ifndef ZTS
459 DATEG(default_timezone) = NULL;
460 #endif
461
462 return SUCCESS;
463 }
464 /* }}} */
465
466 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(date)467 PHP_MINFO_FUNCTION(date)
468 {
469 const timelib_tzdb *tzdb = DATE_TIMEZONEDB;
470
471 php_info_print_table_start();
472 php_info_print_table_row(2, "date/time support", "enabled");
473 php_info_print_table_row(2, "timelib version", TIMELIB_ASCII_VERSION);
474 php_info_print_table_row(2, "\"Olson\" Timezone Database Version", tzdb->version);
475 php_info_print_table_row(2, "Timezone Database", php_date_global_timezone_db_enabled ? "external" : "internal");
476 php_info_print_table_row(2, "Default timezone", guess_timezone(tzdb));
477 php_info_print_table_end();
478
479 DISPLAY_INI_ENTRIES();
480 }
481 /* }}} */
482
483 /* {{{ Timezone Cache functions */
php_date_parse_tzfile(const char * formal_tzname,const timelib_tzdb * tzdb)484 static timelib_tzinfo *php_date_parse_tzfile(const char *formal_tzname, const timelib_tzdb *tzdb)
485 {
486 timelib_tzinfo *tzi;
487 int dummy_error_code;
488
489 if(!DATEG(tzcache)) {
490 ALLOC_HASHTABLE(DATEG(tzcache));
491 zend_hash_init(DATEG(tzcache), 4, NULL, _php_date_tzinfo_dtor, 0);
492 }
493
494 if ((tzi = zend_hash_str_find_ptr(DATEG(tzcache), formal_tzname, strlen(formal_tzname))) != NULL) {
495 return tzi;
496 }
497
498 tzi = timelib_parse_tzfile(formal_tzname, tzdb, &dummy_error_code);
499 if (tzi) {
500 zend_hash_str_add_ptr(DATEG(tzcache), formal_tzname, strlen(formal_tzname), tzi);
501 }
502 return tzi;
503 }
504
php_date_parse_tzfile_wrapper(const char * formal_tzname,const timelib_tzdb * tzdb,int * dummy_error_code)505 timelib_tzinfo *php_date_parse_tzfile_wrapper(const char *formal_tzname, const timelib_tzdb *tzdb, int *dummy_error_code)
506 {
507 return php_date_parse_tzfile(formal_tzname, tzdb);
508 }
509 /* }}} */
510
511 /* Callback to check the date.timezone only when changed increases performance */
512 /* {{{ static PHP_INI_MH(OnUpdate_date_timezone) */
PHP_INI_MH(OnUpdate_date_timezone)513 static PHP_INI_MH(OnUpdate_date_timezone)
514 {
515 if (new_value && !timelib_timezone_id_is_valid(ZSTR_VAL(new_value), DATE_TIMEZONEDB)) {
516 php_error_docref(
517 NULL, E_WARNING,
518 "Invalid date.timezone value '%s', using '%s' instead",
519 ZSTR_VAL(new_value),
520 DATEG(default_timezone) ? DATEG(default_timezone) : "UTC"
521 );
522 return FAILURE;
523 }
524
525 if (OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage) == FAILURE) {
526 return FAILURE;
527 }
528
529 return SUCCESS;
530 }
531 /* }}} */
532
533 /* {{{ Helper functions */
guess_timezone(const timelib_tzdb * tzdb)534 static char* guess_timezone(const timelib_tzdb *tzdb)
535 {
536 /* Checking whether timezone has been set with date_default_timezone_set() */
537 if (DATEG(timezone) && (strlen(DATEG(timezone))) > 0) {
538 return DATEG(timezone);
539 }
540 /* Check config setting for default timezone */
541 if (!DATEG(default_timezone)) {
542 /* Special case: ext/date wasn't initialized yet */
543 zval *ztz;
544
545 if (NULL != (ztz = cfg_get_entry("date.timezone", sizeof("date.timezone")))
546 && Z_TYPE_P(ztz) == IS_STRING && Z_STRLEN_P(ztz) > 0 && timelib_timezone_id_is_valid(Z_STRVAL_P(ztz), tzdb)) {
547 return Z_STRVAL_P(ztz);
548 }
549 } else if (*DATEG(default_timezone)) {
550 return DATEG(default_timezone);
551 }
552 /* Fallback to UTC */
553 return "UTC";
554 }
555
get_timezone_info(void)556 PHPAPI timelib_tzinfo *get_timezone_info(void)
557 {
558 char *tz;
559 timelib_tzinfo *tzi;
560
561 tz = guess_timezone(DATE_TIMEZONEDB);
562 tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB);
563 if (! tzi) {
564 zend_throw_error(NULL, "Timezone database is corrupt. Please file a bug report as this should never happen");
565 }
566 return tzi;
567 }
568
update_property(zend_object * object,zend_string * key,zval * prop_val)569 static void update_property(zend_object *object, zend_string *key, zval *prop_val)
570 {
571 if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') { // not public
572 const char *class_name, *prop_name;
573 size_t prop_name_len;
574
575 if (zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len) == SUCCESS) {
576 if (class_name[0] != '*') { // private
577 zend_string *cname;
578 zend_class_entry *ce;
579
580 cname = zend_string_init(class_name, strlen(class_name), 0);
581 ce = zend_lookup_class(cname);
582
583 if (ce) {
584 zend_update_property(ce, object, prop_name, prop_name_len, prop_val);
585 }
586
587 zend_string_release_ex(cname, 0);
588 } else { // protected
589 zend_update_property(object->ce, object, prop_name, prop_name_len, prop_val);
590 }
591 }
592 return;
593 }
594
595 // public
596 zend_update_property(object->ce, object, ZSTR_VAL(key), ZSTR_LEN(key), prop_val);
597 }
598 /* }}} */
599
600
601 /* {{{ date() and gmdate() data */
602 #include "zend_smart_str.h"
603
604 static const char * const mon_full_names[] = {
605 "January", "February", "March", "April",
606 "May", "June", "July", "August",
607 "September", "October", "November", "December"
608 };
609
610 static const char * const mon_short_names[] = {
611 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
612 };
613
614 static const char * const day_full_names[] = {
615 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
616 };
617
618 static const char * const day_short_names[] = {
619 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
620 };
621
english_suffix(timelib_sll number)622 static char *english_suffix(timelib_sll number)
623 {
624 if (number >= 10 && number <= 19) {
625 return "th";
626 } else {
627 switch (number % 10) {
628 case 1: return "st";
629 case 2: return "nd";
630 case 3: return "rd";
631 }
632 }
633 return "th";
634 }
635 /* }}} */
636
637 /* {{{ day of week helpers */
php_date_full_day_name(timelib_sll y,timelib_sll m,timelib_sll d)638 static const char *php_date_full_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
639 {
640 timelib_sll day_of_week = timelib_day_of_week(y, m, d);
641 if (day_of_week < 0) {
642 return "Unknown";
643 }
644 return day_full_names[day_of_week];
645 }
646
php_date_short_day_name(timelib_sll y,timelib_sll m,timelib_sll d)647 static const char *php_date_short_day_name(timelib_sll y, timelib_sll m, timelib_sll d)
648 {
649 timelib_sll day_of_week = timelib_day_of_week(y, m, d);
650 if (day_of_week < 0) {
651 return "Unknown";
652 }
653 return day_short_names[day_of_week];
654 }
655 /* }}} */
656
657 /* {{{ date_format - (gm)date helper */
date_format(const char * format,size_t format_len,timelib_time * t,bool localtime)658 static zend_string *date_format(const char *format, size_t format_len, timelib_time *t, bool localtime)
659 {
660 smart_str string = {0};
661 size_t i;
662 int length = 0;
663 char buffer[97];
664 timelib_time_offset *offset = NULL;
665 timelib_sll isoweek, isoyear;
666 bool rfc_colon;
667 int weekYearSet = 0;
668
669 if (!format_len) {
670 return ZSTR_EMPTY_ALLOC();
671 }
672
673 if (localtime) {
674 if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
675 offset = timelib_time_offset_ctor();
676 offset->offset = (t->z + (t->dst * 3600));
677 offset->leap_secs = 0;
678 offset->is_dst = t->dst;
679 offset->abbr = timelib_strdup(t->tz_abbr);
680 } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
681 offset = timelib_time_offset_ctor();
682 offset->offset = (t->z);
683 offset->leap_secs = 0;
684 offset->is_dst = 0;
685 offset->abbr = timelib_malloc(9); /* GMT±xxxx\0 */
686 snprintf(offset->abbr, 9, "GMT%c%02d%02d",
687 (offset->offset < 0) ? '-' : '+',
688 abs(offset->offset / 3600),
689 abs((offset->offset % 3600) / 60));
690 } else if (t->zone_type == TIMELIB_ZONETYPE_ID) {
691 offset = timelib_get_time_zone_info(t->sse, t->tz_info);
692 } else {
693 /* Shouldn't happen, but code defensively */
694 offset = timelib_time_offset_ctor();
695 }
696 }
697
698 for (i = 0; i < format_len; i++) {
699 rfc_colon = 0;
700 switch (format[i]) {
701 /* day */
702 case 'd': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->d); break;
703 case 'D': length = slprintf(buffer, sizeof(buffer), "%s", php_date_short_day_name(t->y, t->m, t->d)); break;
704 case 'j': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->d); break;
705 case 'l': length = slprintf(buffer, sizeof(buffer), "%s", php_date_full_day_name(t->y, t->m, t->d)); break;
706 case 'S': length = slprintf(buffer, sizeof(buffer), "%s", english_suffix(t->d)); break;
707 case 'w': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_day_of_week(t->y, t->m, t->d)); break;
708 case 'N': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_iso_day_of_week(t->y, t->m, t->d)); break;
709 case 'z': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_day_of_year(t->y, t->m, t->d)); break;
710
711 /* week */
712 case 'W':
713 if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
714 length = slprintf(buffer, sizeof(buffer), "%02d", (int) isoweek); break; /* iso weeknr */
715 case 'o':
716 if(!weekYearSet) { timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear); weekYearSet = 1; }
717 length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) isoyear); break; /* iso year */
718
719 /* month */
720 case 'F': length = slprintf(buffer, sizeof(buffer), "%s", mon_full_names[t->m - 1]); break;
721 case 'm': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->m); break;
722 case 'M': length = slprintf(buffer, sizeof(buffer), "%s", mon_short_names[t->m - 1]); break;
723 case 'n': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->m); break;
724 case 't': length = slprintf(buffer, sizeof(buffer), "%d", (int) timelib_days_in_month(t->y, t->m)); break;
725
726 /* year */
727 case 'L': length = slprintf(buffer, sizeof(buffer), "%d", timelib_is_leap((int) t->y)); break;
728 case 'y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) (t->y % 100)); break;
729 case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break;
730 case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break;
731 case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break;
732
733 /* time */
734 case 'a': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "pm" : "am"); break;
735 case 'A': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "PM" : "AM"); break;
736 case 'B': {
737 int retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
738 if (retval < 0) {
739 retval += 864000;
740 }
741 /* Make sure to do this on a positive int to avoid rounding errors */
742 retval = (retval / 864) % 1000;
743 length = slprintf(buffer, sizeof(buffer), "%03d", retval);
744 break;
745 }
746 case 'g': length = slprintf(buffer, sizeof(buffer), "%d", (t->h % 12) ? (int) t->h % 12 : 12); break;
747 case 'G': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->h); break;
748 case 'h': length = slprintf(buffer, sizeof(buffer), "%02d", (t->h % 12) ? (int) t->h % 12 : 12); break;
749 case 'H': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->h); break;
750 case 'i': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->i); break;
751 case 's': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->s); break;
752 case 'u': length = slprintf(buffer, sizeof(buffer), "%06d", (int) floor(t->us)); break;
753 case 'v': length = slprintf(buffer, sizeof(buffer), "%03d", (int) floor(t->us / 1000)); break;
754
755 /* timezone */
756 case 'I': length = slprintf(buffer, sizeof(buffer), "%d", localtime ? offset->is_dst : 0); break;
757 case 'p':
758 if (!localtime || strcmp(offset->abbr, "UTC") == 0 || strcmp(offset->abbr, "Z") == 0 || strcmp(offset->abbr, "GMT+0000") == 0) {
759 length = slprintf(buffer, sizeof(buffer), "%s", "Z");
760 break;
761 }
762 ZEND_FALLTHROUGH;
763 case 'P': rfc_colon = 1; ZEND_FALLTHROUGH;
764 case 'O': length = slprintf(buffer, sizeof(buffer), "%c%02d%s%02d",
765 localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
766 localtime ? abs(offset->offset / 3600) : 0,
767 rfc_colon ? ":" : "",
768 localtime ? abs((offset->offset % 3600) / 60) : 0
769 );
770 break;
771 case 'T': length = slprintf(buffer, sizeof(buffer), "%s", localtime ? offset->abbr : "GMT"); break;
772 case 'e': if (!localtime) {
773 length = slprintf(buffer, sizeof(buffer), "%s", "UTC");
774 } else {
775 switch (t->zone_type) {
776 case TIMELIB_ZONETYPE_ID:
777 length = slprintf(buffer, sizeof(buffer), "%s", t->tz_info->name);
778 break;
779 case TIMELIB_ZONETYPE_ABBR:
780 length = slprintf(buffer, sizeof(buffer), "%s", offset->abbr);
781 break;
782 case TIMELIB_ZONETYPE_OFFSET:
783 length = slprintf(buffer, sizeof(buffer), "%c%02d:%02d",
784 ((offset->offset < 0) ? '-' : '+'),
785 abs(offset->offset / 3600),
786 abs((offset->offset % 3600) / 60)
787 );
788 break;
789 }
790 }
791 break;
792 case 'Z': length = slprintf(buffer, sizeof(buffer), "%d", localtime ? offset->offset : 0); break;
793
794 /* full date/time */
795 case 'c': length = slprintf(buffer, sizeof(buffer), "%04" ZEND_LONG_FMT_SPEC "-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
796 (zend_long) t->y, (int) t->m, (int) t->d,
797 (int) t->h, (int) t->i, (int) t->s,
798 localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
799 localtime ? abs(offset->offset / 3600) : 0,
800 localtime ? abs((offset->offset % 3600) / 60) : 0
801 );
802 break;
803 case 'r': length = slprintf(buffer, sizeof(buffer), "%3s, %02d %3s %04" ZEND_LONG_FMT_SPEC " %02d:%02d:%02d %c%02d%02d",
804 php_date_short_day_name(t->y, t->m, t->d),
805 (int) t->d, mon_short_names[t->m - 1],
806 (zend_long) t->y, (int) t->h, (int) t->i, (int) t->s,
807 localtime ? ((offset->offset < 0) ? '-' : '+') : '+',
808 localtime ? abs(offset->offset / 3600) : 0,
809 localtime ? abs((offset->offset % 3600) / 60) : 0
810 );
811 break;
812 case 'U': length = slprintf(buffer, sizeof(buffer), "%lld", (timelib_sll) t->sse); break;
813
814 case '\\': if (i < format_len) i++; ZEND_FALLTHROUGH;
815
816 default: buffer[0] = format[i]; buffer[1] = '\0'; length = 1; break;
817 }
818 smart_str_appendl(&string, buffer, length);
819 }
820
821 smart_str_0(&string);
822
823 if (localtime) {
824 timelib_time_offset_dtor(offset);
825 }
826
827 return string.s;
828 }
829
php_date(INTERNAL_FUNCTION_PARAMETERS,bool localtime)830 static void php_date(INTERNAL_FUNCTION_PARAMETERS, bool localtime)
831 {
832 zend_string *format;
833 zend_long ts;
834 bool ts_is_null = 1;
835
836 ZEND_PARSE_PARAMETERS_START(1, 2)
837 Z_PARAM_STR(format)
838 Z_PARAM_OPTIONAL
839 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
840 ZEND_PARSE_PARAMETERS_END();
841
842 if (ts_is_null) {
843 ts = php_time();
844 }
845
846 RETURN_STR(php_format_date(ZSTR_VAL(format), ZSTR_LEN(format), ts, localtime));
847 }
848 /* }}} */
849
php_format_date(const char * format,size_t format_len,time_t ts,bool localtime)850 PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_t ts, bool localtime) /* {{{ */
851 {
852 timelib_time *t;
853 timelib_tzinfo *tzi;
854 zend_string *string;
855
856 t = timelib_time_ctor();
857
858 if (localtime) {
859 tzi = get_timezone_info();
860 t->tz_info = tzi;
861 t->zone_type = TIMELIB_ZONETYPE_ID;
862 timelib_unixtime2local(t, ts);
863 } else {
864 tzi = NULL;
865 timelib_unixtime2gmt(t, ts);
866 }
867
868 string = date_format(format, format_len, t, localtime);
869
870 timelib_time_dtor(t);
871 return string;
872 }
873 /* }}} */
874
875 /* {{{ php_idate */
php_idate(char format,time_t ts,bool localtime)876 PHPAPI int php_idate(char format, time_t ts, bool localtime)
877 {
878 timelib_time *t;
879 timelib_tzinfo *tzi;
880 int retval = -1;
881 timelib_time_offset *offset = NULL;
882 timelib_sll isoweek, isoyear;
883
884 t = timelib_time_ctor();
885
886 if (!localtime) {
887 tzi = get_timezone_info();
888 t->tz_info = tzi;
889 t->zone_type = TIMELIB_ZONETYPE_ID;
890 timelib_unixtime2local(t, ts);
891 } else {
892 tzi = NULL;
893 timelib_unixtime2gmt(t, ts);
894 }
895
896 if (!localtime) {
897 if (t->zone_type == TIMELIB_ZONETYPE_ABBR) {
898 offset = timelib_time_offset_ctor();
899 offset->offset = (t->z + (t->dst * 3600));
900 offset->leap_secs = 0;
901 offset->is_dst = t->dst;
902 offset->abbr = timelib_strdup(t->tz_abbr);
903 } else if (t->zone_type == TIMELIB_ZONETYPE_OFFSET) {
904 offset = timelib_time_offset_ctor();
905 offset->offset = (t->z + (t->dst * 3600));
906 offset->leap_secs = 0;
907 offset->is_dst = t->dst;
908 offset->abbr = timelib_malloc(9); /* GMT±xxxx\0 */
909 snprintf(offset->abbr, 9, "GMT%c%02d%02d",
910 (offset->offset < 0) ? '-' : '+',
911 abs(offset->offset / 3600),
912 abs((offset->offset % 3600) / 60));
913 } else {
914 offset = timelib_get_time_zone_info(t->sse, t->tz_info);
915 }
916 }
917
918 timelib_isoweek_from_date(t->y, t->m, t->d, &isoweek, &isoyear);
919
920 switch (format) {
921 /* day */
922 case 'd': case 'j': retval = (int) t->d; break;
923
924 case 'N': retval = (int) timelib_iso_day_of_week(t->y, t->m, t->d); break;
925 case 'w': retval = (int) timelib_day_of_week(t->y, t->m, t->d); break;
926 case 'z': retval = (int) timelib_day_of_year(t->y, t->m, t->d); break;
927
928 /* week */
929 case 'W': retval = (int) isoweek; break; /* iso weeknr */
930
931 /* month */
932 case 'm': case 'n': retval = (int) t->m; break;
933 case 't': retval = (int) timelib_days_in_month(t->y, t->m); break;
934
935 /* year */
936 case 'L': retval = (int) timelib_is_leap((int) t->y); break;
937 case 'y': retval = (int) (t->y % 100); break;
938 case 'Y': retval = (int) t->y; break;
939 case 'o': retval = (int) isoyear; break; /* iso year */
940
941 /* Swatch Beat a.k.a. Internet Time */
942 case 'B':
943 retval = ((((long)t->sse)-(((long)t->sse) - ((((long)t->sse) % 86400) + 3600))) * 10);
944 if (retval < 0) {
945 retval += 864000;
946 }
947 /* Make sure to do this on a positive int to avoid rounding errors */
948 retval = (retval / 864) % 1000;
949 break;
950
951 /* time */
952 case 'g': case 'h': retval = (int) ((t->h % 12) ? (int) t->h % 12 : 12); break;
953 case 'H': case 'G': retval = (int) t->h; break;
954 case 'i': retval = (int) t->i; break;
955 case 's': retval = (int) t->s; break;
956
957 /* timezone */
958 case 'I': retval = (int) (!localtime ? offset->is_dst : 0); break;
959 case 'Z': retval = (int) (!localtime ? offset->offset : 0); break;
960
961 case 'U': retval = (int) t->sse; break;
962 }
963
964 if (!localtime) {
965 timelib_time_offset_dtor(offset);
966 }
967 timelib_time_dtor(t);
968
969 return retval;
970 }
971 /* }}} */
972
973 /* {{{ Format a local date/time */
PHP_FUNCTION(date)974 PHP_FUNCTION(date)
975 {
976 php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
977 }
978 /* }}} */
979
980 /* {{{ Format a GMT date/time */
PHP_FUNCTION(gmdate)981 PHP_FUNCTION(gmdate)
982 {
983 php_date(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
984 }
985 /* }}} */
986
987 /* {{{ Format a local time/date as integer */
PHP_FUNCTION(idate)988 PHP_FUNCTION(idate)
989 {
990 zend_string *format;
991 zend_long ts;
992 bool ts_is_null = 1;
993 int ret;
994
995 ZEND_PARSE_PARAMETERS_START(1, 2)
996 Z_PARAM_STR(format)
997 Z_PARAM_OPTIONAL
998 Z_PARAM_LONG_OR_NULL(ts, ts_is_null)
999 ZEND_PARSE_PARAMETERS_END();
1000
1001 if (ZSTR_LEN(format) != 1) {
1002 php_error_docref(NULL, E_WARNING, "idate format is one char");
1003 RETURN_FALSE;
1004 }
1005
1006 if (ts_is_null) {
1007 ts = php_time();
1008 }
1009
1010 ret = php_idate(ZSTR_VAL(format)[0], ts, 0);
1011 if (ret == -1) {
1012 php_error_docref(NULL, E_WARNING, "Unrecognized date format token");
1013 RETURN_FALSE;
1014 }
1015 RETURN_LONG(ret);
1016 }
1017 /* }}} */
1018
1019 /* {{{ php_date_set_tzdb - NOT THREADSAFE */
php_date_set_tzdb(timelib_tzdb * tzdb)1020 PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb)
1021 {
1022 const timelib_tzdb *builtin = timelib_builtin_db();
1023
1024 if (php_version_compare(tzdb->version, builtin->version) > 0) {
1025 php_date_global_timezone_db = tzdb;
1026 php_date_global_timezone_db_enabled = 1;
1027 }
1028 }
1029 /* }}} */
1030
1031 /* {{{ php_parse_date: Backwards compatibility function */
php_parse_date(const char * string,zend_long * now)1032 PHPAPI zend_long php_parse_date(const char *string, zend_long *now)
1033 {
1034 timelib_time *parsed_time;
1035 timelib_error_container *error = NULL;
1036 int error2;
1037 zend_long retval;
1038
1039 parsed_time = timelib_strtotime(string, strlen(string), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1040 if (error->error_count) {
1041 timelib_time_dtor(parsed_time);
1042 timelib_error_container_dtor(error);
1043 return -1;
1044 }
1045 timelib_error_container_dtor(error);
1046 timelib_update_ts(parsed_time, NULL);
1047 retval = timelib_date_to_int(parsed_time, &error2);
1048 timelib_time_dtor(parsed_time);
1049 if (error2) {
1050 return -1;
1051 }
1052 return retval;
1053 }
1054 /* }}} */
1055
1056 /* {{{ Convert string representation of date and time to a timestamp */
PHP_FUNCTION(strtotime)1057 PHP_FUNCTION(strtotime)
1058 {
1059 zend_string *times;
1060 int parse_error, epoch_does_not_fit_in_zend_long;
1061 timelib_error_container *error;
1062 zend_long preset_ts, ts;
1063 bool preset_ts_is_null = 1;
1064 timelib_time *t, *now;
1065 timelib_tzinfo *tzi;
1066
1067 ZEND_PARSE_PARAMETERS_START(1, 2)
1068 Z_PARAM_STR(times)
1069 Z_PARAM_OPTIONAL
1070 Z_PARAM_LONG_OR_NULL(preset_ts, preset_ts_is_null)
1071 ZEND_PARSE_PARAMETERS_END();
1072
1073 /* timelib_strtotime() expects the string to not be empty */
1074 if (ZSTR_LEN(times) == 0) {
1075 RETURN_FALSE;
1076 }
1077
1078 tzi = get_timezone_info();
1079 if (!tzi) {
1080 return;
1081 }
1082
1083 now = timelib_time_ctor();
1084 now->tz_info = tzi;
1085 now->zone_type = TIMELIB_ZONETYPE_ID;
1086 timelib_unixtime2local(now,
1087 !preset_ts_is_null ? (timelib_sll) preset_ts : (timelib_sll) php_time());
1088
1089 t = timelib_strtotime(ZSTR_VAL(times), ZSTR_LEN(times), &error,
1090 DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
1091 parse_error = error->error_count;
1092 timelib_error_container_dtor(error);
1093 if (parse_error) {
1094 timelib_time_dtor(now);
1095 timelib_time_dtor(t);
1096 RETURN_FALSE;
1097 }
1098
1099 timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
1100 timelib_update_ts(t, tzi);
1101 ts = timelib_date_to_int(t, &epoch_does_not_fit_in_zend_long);
1102
1103 timelib_time_dtor(now);
1104 timelib_time_dtor(t);
1105
1106 if (epoch_does_not_fit_in_zend_long) {
1107 php_error_docref(NULL, E_WARNING, "Epoch doesn't fit in a PHP integer");
1108 RETURN_FALSE;
1109 }
1110
1111 RETURN_LONG(ts);
1112 }
1113 /* }}} */
1114
1115 /* {{{ php_mktime - (gm)mktime helper */
php_mktime(INTERNAL_FUNCTION_PARAMETERS,bool gmt)1116 PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, bool gmt)
1117 {
1118 zend_long hou, min, sec, mon, day, yea;
1119 bool min_is_null = 1, sec_is_null = 1, mon_is_null = 1, day_is_null = 1, yea_is_null = 1;
1120 timelib_time *now;
1121 timelib_tzinfo *tzi = NULL;
1122 zend_long ts, adjust_seconds = 0;
1123 int epoch_does_not_fit_in_zend_long;
1124
1125 ZEND_PARSE_PARAMETERS_START(1, 6)
1126 Z_PARAM_LONG(hou)
1127 Z_PARAM_OPTIONAL
1128 Z_PARAM_LONG_OR_NULL(min, min_is_null)
1129 Z_PARAM_LONG_OR_NULL(sec, sec_is_null)
1130 Z_PARAM_LONG_OR_NULL(mon, mon_is_null)
1131 Z_PARAM_LONG_OR_NULL(day, day_is_null)
1132 Z_PARAM_LONG_OR_NULL(yea, yea_is_null)
1133 ZEND_PARSE_PARAMETERS_END();
1134
1135 /* Initialize structure with current time */
1136 now = timelib_time_ctor();
1137 if (gmt) {
1138 timelib_unixtime2gmt(now, (timelib_sll) php_time());
1139 } else {
1140 tzi = get_timezone_info();
1141 if (!tzi) {
1142 return;
1143 }
1144 now->tz_info = tzi;
1145 now->zone_type = TIMELIB_ZONETYPE_ID;
1146 timelib_unixtime2local(now, (timelib_sll) php_time());
1147 }
1148
1149 now->h = hou;
1150
1151 if (!min_is_null) {
1152 now->i = min;
1153 }
1154
1155 if (!sec_is_null) {
1156 now->s = sec;
1157 }
1158
1159 if (!mon_is_null) {
1160 now->m = mon;
1161 }
1162
1163 if (!day_is_null) {
1164 now->d = day;
1165 }
1166
1167 if (!yea_is_null) {
1168 if (yea >= 0 && yea < 70) {
1169 yea += 2000;
1170 } else if (yea >= 70 && yea <= 100) {
1171 yea += 1900;
1172 }
1173 now->y = yea;
1174 }
1175
1176 /* Update the timestamp */
1177 if (gmt) {
1178 timelib_update_ts(now, NULL);
1179 } else {
1180 timelib_update_ts(now, tzi);
1181 }
1182
1183 /* Clean up and return */
1184 ts = timelib_date_to_int(now, &epoch_does_not_fit_in_zend_long);
1185
1186 if (epoch_does_not_fit_in_zend_long) {
1187 timelib_time_dtor(now);
1188 php_error_docref(NULL, E_WARNING, "Epoch doesn't fit in a PHP integer");
1189 RETURN_FALSE;
1190 }
1191
1192 ts += adjust_seconds;
1193 timelib_time_dtor(now);
1194
1195 RETURN_LONG(ts);
1196 }
1197 /* }}} */
1198
1199 /* {{{ Get UNIX timestamp for a date */
PHP_FUNCTION(mktime)1200 PHP_FUNCTION(mktime)
1201 {
1202 php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1203 }
1204 /* }}} */
1205
1206 /* {{{ Get UNIX timestamp for a GMT date */
PHP_FUNCTION(gmmktime)1207 PHP_FUNCTION(gmmktime)
1208 {
1209 php_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1210 }
1211 /* }}} */
1212
1213 /* {{{ Returns true(1) if it is a valid date in gregorian calendar */
PHP_FUNCTION(checkdate)1214 PHP_FUNCTION(checkdate)
1215 {
1216 zend_long m, d, y;
1217
1218 ZEND_PARSE_PARAMETERS_START(3, 3)
1219 Z_PARAM_LONG(m)
1220 Z_PARAM_LONG(d)
1221 Z_PARAM_LONG(y)
1222 ZEND_PARSE_PARAMETERS_END();
1223
1224 if (y < 1 || y > 32767 || !timelib_valid_date(y, m, d)) {
1225 RETURN_FALSE;
1226 }
1227 RETURN_TRUE; /* True : This month, day, year arguments are valid */
1228 }
1229 /* }}} */
1230
1231 /* {{{ php_strftime - (gm)strftime helper */
php_strftime(INTERNAL_FUNCTION_PARAMETERS,bool gmt)1232 PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, bool gmt)
1233 {
1234 zend_string *format;
1235 zend_long timestamp;
1236 bool timestamp_is_null = 1;
1237 struct tm ta;
1238 int max_reallocs = 5;
1239 size_t buf_len = 256, real_len;
1240 timelib_time *ts;
1241 timelib_tzinfo *tzi;
1242 timelib_time_offset *offset = NULL;
1243 zend_string *buf;
1244
1245 ZEND_PARSE_PARAMETERS_START(1, 2)
1246 Z_PARAM_STR(format)
1247 Z_PARAM_OPTIONAL
1248 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
1249 ZEND_PARSE_PARAMETERS_END();
1250
1251 if (ZSTR_LEN(format) == 0) {
1252 RETURN_FALSE;
1253 }
1254
1255 if (timestamp_is_null) {
1256 timestamp = (zend_long) php_time();
1257 }
1258
1259 ts = timelib_time_ctor();
1260 if (gmt) {
1261 tzi = NULL;
1262 timelib_unixtime2gmt(ts, (timelib_sll) timestamp);
1263 } else {
1264 tzi = get_timezone_info();
1265 if (!tzi) {
1266 return;
1267 }
1268 ts->tz_info = tzi;
1269 ts->zone_type = TIMELIB_ZONETYPE_ID;
1270 timelib_unixtime2local(ts, (timelib_sll) timestamp);
1271 }
1272 ta.tm_sec = ts->s;
1273 ta.tm_min = ts->i;
1274 ta.tm_hour = ts->h;
1275 ta.tm_mday = ts->d;
1276 ta.tm_mon = ts->m - 1;
1277 ta.tm_year = ts->y - 1900;
1278 ta.tm_wday = timelib_day_of_week(ts->y, ts->m, ts->d);
1279 ta.tm_yday = timelib_day_of_year(ts->y, ts->m, ts->d);
1280 if (gmt) {
1281 ta.tm_isdst = 0;
1282 #if HAVE_STRUCT_TM_TM_GMTOFF
1283 ta.tm_gmtoff = 0;
1284 #endif
1285 #if HAVE_STRUCT_TM_TM_ZONE
1286 ta.tm_zone = "GMT";
1287 #endif
1288 } else {
1289 offset = timelib_get_time_zone_info(timestamp, tzi);
1290
1291 ta.tm_isdst = offset->is_dst;
1292 #if HAVE_STRUCT_TM_TM_GMTOFF
1293 ta.tm_gmtoff = offset->offset;
1294 #endif
1295 #if HAVE_STRUCT_TM_TM_ZONE
1296 ta.tm_zone = offset->abbr;
1297 #endif
1298 }
1299
1300 /* VS2012 crt has a bug where strftime crash with %z and %Z format when the
1301 initial buffer is too small. See
1302 http://connect.microsoft.com/VisualStudio/feedback/details/759720/vs2012-strftime-crash-with-z-formatting-code */
1303 buf = zend_string_alloc(buf_len, 0);
1304 while ((real_len = strftime(ZSTR_VAL(buf), buf_len, ZSTR_VAL(format), &ta)) == buf_len || real_len == 0) {
1305 buf_len *= 2;
1306 buf = zend_string_extend(buf, buf_len, 0);
1307 if (!--max_reallocs) {
1308 break;
1309 }
1310 }
1311 #ifdef PHP_WIN32
1312 /* VS2012 strftime() returns number of characters, not bytes.
1313 See VC++11 bug id 766205. */
1314 if (real_len > 0) {
1315 real_len = strlen(buf->val);
1316 }
1317 #endif
1318
1319 timelib_time_dtor(ts);
1320 if (!gmt) {
1321 timelib_time_offset_dtor(offset);
1322 }
1323
1324 if (real_len && real_len != buf_len) {
1325 buf = zend_string_truncate(buf, real_len, 0);
1326 RETURN_NEW_STR(buf);
1327 }
1328 zend_string_efree(buf);
1329 RETURN_FALSE;
1330 }
1331 /* }}} */
1332
1333 /* {{{ Format a local time/date according to locale settings */
PHP_FUNCTION(strftime)1334 PHP_FUNCTION(strftime)
1335 {
1336 php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1337 }
1338 /* }}} */
1339
1340 /* {{{ Format a GMT/UCT time/date according to locale settings */
PHP_FUNCTION(gmstrftime)1341 PHP_FUNCTION(gmstrftime)
1342 {
1343 php_strftime(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1344 }
1345 /* }}} */
1346
1347 /* {{{ Return current UNIX timestamp */
PHP_FUNCTION(time)1348 PHP_FUNCTION(time)
1349 {
1350 ZEND_PARSE_PARAMETERS_NONE();
1351
1352 RETURN_LONG((zend_long)php_time());
1353 }
1354 /* }}} */
1355
1356 /* {{{ Returns the results of the C system call localtime as an associative array if the associative_array argument is set to 1 other wise it is a regular array */
PHP_FUNCTION(localtime)1357 PHP_FUNCTION(localtime)
1358 {
1359 zend_long timestamp;
1360 bool timestamp_is_null = 1;
1361 bool associative = 0;
1362 timelib_tzinfo *tzi;
1363 timelib_time *ts;
1364
1365 ZEND_PARSE_PARAMETERS_START(0, 2)
1366 Z_PARAM_OPTIONAL
1367 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
1368 Z_PARAM_BOOL(associative)
1369 ZEND_PARSE_PARAMETERS_END();
1370
1371 if (timestamp_is_null) {
1372 timestamp = (zend_long) php_time();
1373 }
1374
1375 tzi = get_timezone_info();
1376 if (!tzi) {
1377 RETURN_THROWS();
1378 }
1379 ts = timelib_time_ctor();
1380 ts->tz_info = tzi;
1381 ts->zone_type = TIMELIB_ZONETYPE_ID;
1382 timelib_unixtime2local(ts, (timelib_sll) timestamp);
1383
1384 array_init(return_value);
1385
1386 if (associative) {
1387 add_assoc_long(return_value, "tm_sec", ts->s);
1388 add_assoc_long(return_value, "tm_min", ts->i);
1389 add_assoc_long(return_value, "tm_hour", ts->h);
1390 add_assoc_long(return_value, "tm_mday", ts->d);
1391 add_assoc_long(return_value, "tm_mon", ts->m - 1);
1392 add_assoc_long(return_value, "tm_year", ts->y - 1900);
1393 add_assoc_long(return_value, "tm_wday", timelib_day_of_week(ts->y, ts->m, ts->d));
1394 add_assoc_long(return_value, "tm_yday", timelib_day_of_year(ts->y, ts->m, ts->d));
1395 add_assoc_long(return_value, "tm_isdst", ts->dst);
1396 } else {
1397 add_next_index_long(return_value, ts->s);
1398 add_next_index_long(return_value, ts->i);
1399 add_next_index_long(return_value, ts->h);
1400 add_next_index_long(return_value, ts->d);
1401 add_next_index_long(return_value, ts->m - 1);
1402 add_next_index_long(return_value, ts->y- 1900);
1403 add_next_index_long(return_value, timelib_day_of_week(ts->y, ts->m, ts->d));
1404 add_next_index_long(return_value, timelib_day_of_year(ts->y, ts->m, ts->d));
1405 add_next_index_long(return_value, ts->dst);
1406 }
1407
1408 timelib_time_dtor(ts);
1409 }
1410 /* }}} */
1411
1412 /* {{{ Get date/time information */
PHP_FUNCTION(getdate)1413 PHP_FUNCTION(getdate)
1414 {
1415 zend_long timestamp;
1416 bool timestamp_is_null = 1;
1417 timelib_tzinfo *tzi;
1418 timelib_time *ts;
1419
1420 ZEND_PARSE_PARAMETERS_START(0, 1)
1421 Z_PARAM_OPTIONAL
1422 Z_PARAM_LONG_OR_NULL(timestamp, timestamp_is_null)
1423 ZEND_PARSE_PARAMETERS_END();
1424
1425 if (timestamp_is_null) {
1426 timestamp = (zend_long) php_time();
1427 }
1428
1429 tzi = get_timezone_info();
1430 if (!tzi) {
1431 RETURN_THROWS();
1432 }
1433 ts = timelib_time_ctor();
1434 ts->tz_info = tzi;
1435 ts->zone_type = TIMELIB_ZONETYPE_ID;
1436 timelib_unixtime2local(ts, (timelib_sll) timestamp);
1437
1438 array_init(return_value);
1439
1440 add_assoc_long(return_value, "seconds", ts->s);
1441 add_assoc_long(return_value, "minutes", ts->i);
1442 add_assoc_long(return_value, "hours", ts->h);
1443 add_assoc_long(return_value, "mday", ts->d);
1444 add_assoc_long(return_value, "wday", timelib_day_of_week(ts->y, ts->m, ts->d));
1445 add_assoc_long(return_value, "mon", ts->m);
1446 add_assoc_long(return_value, "year", ts->y);
1447 add_assoc_long(return_value, "yday", timelib_day_of_year(ts->y, ts->m, ts->d));
1448 add_assoc_string(return_value, "weekday", php_date_full_day_name(ts->y, ts->m, ts->d));
1449 add_assoc_string(return_value, "month", mon_full_names[ts->m - 1]);
1450 add_index_long(return_value, 0, timestamp);
1451
1452 timelib_time_dtor(ts);
1453 }
1454 /* }}} */
1455
create_date_period_datetime(timelib_time * datetime,zend_class_entry * ce,zval * zv)1456 static void create_date_period_datetime(timelib_time *datetime, zend_class_entry *ce, zval *zv)
1457 {
1458 if (datetime) {
1459 php_date_obj *date_obj;
1460
1461 object_init_ex(zv, ce);
1462 date_obj = Z_PHPDATE_P(zv);
1463 date_obj->time = timelib_time_clone(datetime);
1464 } else {
1465 ZVAL_NULL(zv);
1466 }
1467 }
1468
create_date_period_interval(timelib_rel_time * interval,zval * zv)1469 static void create_date_period_interval(timelib_rel_time *interval, zval *zv)
1470 {
1471 if (interval) {
1472 php_interval_obj *interval_obj;
1473
1474 object_init_ex(zv, date_ce_interval);
1475 interval_obj = Z_PHPINTERVAL_P(zv);
1476 interval_obj->diff = timelib_rel_time_clone(interval);
1477 interval_obj->initialized = 1;
1478 } else {
1479 ZVAL_NULL(zv);
1480 }
1481 }
1482
write_date_period_property(zend_object * obj,const char * name,const size_t name_len,zval * zv)1483 static void write_date_period_property(zend_object *obj, const char *name, const size_t name_len, zval *zv)
1484 {
1485 zend_string *property_name = zend_string_init(name, name_len, 0);
1486
1487 zend_std_write_property(obj, property_name, zv, NULL);
1488
1489 zval_ptr_dtor(zv);
1490 zend_string_release(property_name);
1491 }
1492
initialize_date_period_properties(php_period_obj * period_obj)1493 static void initialize_date_period_properties(php_period_obj *period_obj)
1494 {
1495 zval zv;
1496
1497 if (UNEXPECTED(!period_obj->std.properties)) {
1498 rebuild_object_properties(&period_obj->std);
1499 }
1500
1501 create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
1502 write_date_period_property(&period_obj->std, "start", sizeof("start") - 1, &zv);
1503
1504 create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
1505 write_date_period_property(&period_obj->std, "current", sizeof("current") - 1, &zv);
1506
1507 create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
1508 write_date_period_property(&period_obj->std, "end", sizeof("end") - 1, &zv);
1509
1510 create_date_period_interval(period_obj->interval, &zv);
1511 write_date_period_property(&period_obj->std, "interval", sizeof("interval") - 1, &zv);
1512
1513 ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
1514 write_date_period_property(&period_obj->std, "recurrences", sizeof("recurrences") - 1, &zv);
1515
1516 ZVAL_BOOL(&zv, period_obj->include_start_date);
1517 write_date_period_property(&period_obj->std, "include_start_date", sizeof("include_start_date") - 1, &zv);
1518
1519 ZVAL_BOOL(&zv, period_obj->include_end_date);
1520 write_date_period_property(&period_obj->std, "include_end_date", sizeof("include_end_date") - 1, &zv);
1521 }
1522
1523 /* define an overloaded iterator structure */
1524 typedef struct {
1525 zend_object_iterator intern;
1526 zval current;
1527 php_period_obj *object;
1528 int current_index;
1529 } date_period_it;
1530
1531 /* {{{ date_period_it_invalidate_current */
date_period_it_invalidate_current(zend_object_iterator * iter)1532 static void date_period_it_invalidate_current(zend_object_iterator *iter)
1533 {
1534 date_period_it *iterator = (date_period_it *)iter;
1535
1536 if (Z_TYPE(iterator->current) != IS_UNDEF) {
1537 zval_ptr_dtor(&iterator->current);
1538 ZVAL_UNDEF(&iterator->current);
1539 }
1540 }
1541 /* }}} */
1542
1543 /* {{{ date_period_it_dtor */
date_period_it_dtor(zend_object_iterator * iter)1544 static void date_period_it_dtor(zend_object_iterator *iter)
1545 {
1546 date_period_it *iterator = (date_period_it *)iter;
1547
1548 date_period_it_invalidate_current(iter);
1549
1550 zval_ptr_dtor(&iterator->intern.data);
1551 }
1552 /* }}} */
1553
1554 /* {{{ date_period_it_has_more */
date_period_it_has_more(zend_object_iterator * iter)1555 static int date_period_it_has_more(zend_object_iterator *iter)
1556 {
1557 date_period_it *iterator = (date_period_it *)iter;
1558 php_period_obj *object = Z_PHPPERIOD_P(&iterator->intern.data);
1559
1560 if (object->end) {
1561 if (object->include_end_date) {
1562 return object->current->sse <= object->end->sse ? SUCCESS : FAILURE;
1563 } else {
1564 return object->current->sse < object->end->sse ? SUCCESS : FAILURE;
1565 }
1566 } else {
1567 return (iterator->current_index < object->recurrences) ? SUCCESS : FAILURE;
1568 }
1569 }
1570 /* }}} */
1571
get_base_date_class(zend_class_entry * start_ce)1572 static zend_class_entry *get_base_date_class(zend_class_entry *start_ce)
1573 {
1574 zend_class_entry *tmp = start_ce;
1575
1576 while (tmp != date_ce_date && tmp != date_ce_immutable && tmp->parent) {
1577 tmp = tmp->parent;
1578 }
1579
1580 return tmp;
1581 }
1582
1583 /* {{{ date_period_it_current_data */
date_period_it_current_data(zend_object_iterator * iter)1584 static zval *date_period_it_current_data(zend_object_iterator *iter)
1585 {
1586 date_period_it *iterator = (date_period_it *)iter;
1587 php_period_obj *object = Z_PHPPERIOD_P(&iterator->intern.data);
1588 timelib_time *it_time = object->current;
1589 php_date_obj *newdateobj;
1590
1591 /* Create new object */
1592 php_date_instantiate(get_base_date_class(object->start_ce), &iterator->current);
1593 newdateobj = Z_PHPDATE_P(&iterator->current);
1594 newdateobj->time = timelib_time_ctor();
1595 *newdateobj->time = *it_time;
1596 if (it_time->tz_abbr) {
1597 newdateobj->time->tz_abbr = timelib_strdup(it_time->tz_abbr);
1598 }
1599 if (it_time->tz_info) {
1600 newdateobj->time->tz_info = it_time->tz_info;
1601 }
1602
1603 return &iterator->current;
1604 }
1605 /* }}} */
1606
1607 /* {{{ date_period_it_current_key */
date_period_it_current_key(zend_object_iterator * iter,zval * key)1608 static void date_period_it_current_key(zend_object_iterator *iter, zval *key)
1609 {
1610 date_period_it *iterator = (date_period_it *)iter;
1611 ZVAL_LONG(key, iterator->current_index);
1612 }
1613 /* }}} */
1614
date_period_advance(timelib_time * it_time,timelib_rel_time * interval)1615 static void date_period_advance(timelib_time *it_time, timelib_rel_time *interval)
1616 {
1617 it_time->have_relative = 1;
1618 it_time->relative = *interval;
1619 it_time->sse_uptodate = 0;
1620 timelib_update_ts(it_time, NULL);
1621 timelib_update_from_sse(it_time);
1622 }
1623
1624 /* {{{ date_period_it_move_forward */
date_period_it_move_forward(zend_object_iterator * iter)1625 static void date_period_it_move_forward(zend_object_iterator *iter)
1626 {
1627 date_period_it *iterator = (date_period_it *)iter;
1628 php_period_obj *object = Z_PHPPERIOD_P(&iterator->intern.data);
1629 timelib_time *it_time = object->current;
1630 zval current_zv;
1631
1632 date_period_advance(it_time, object->interval);
1633
1634 if (UNEXPECTED(!object->std.properties)) {
1635 rebuild_object_properties(&object->std);
1636 }
1637
1638 create_date_period_datetime(object->current, object->start_ce, ¤t_zv);
1639 zend_string *property_name = zend_string_init("current", sizeof("current") - 1, 0);
1640 zend_std_write_property(&object->std, property_name, ¤t_zv, NULL);
1641 zval_ptr_dtor(¤t_zv);
1642 zend_string_release(property_name);
1643
1644 iterator->current_index++;
1645 date_period_it_invalidate_current(iter);
1646 }
1647 /* }}} */
1648
1649 /* {{{ date_period_it_rewind */
date_period_it_rewind(zend_object_iterator * iter)1650 static void date_period_it_rewind(zend_object_iterator *iter)
1651 {
1652 date_period_it *iterator = (date_period_it *)iter;
1653
1654 iterator->current_index = 0;
1655 if (iterator->object->current) {
1656 timelib_time_dtor(iterator->object->current);
1657 }
1658 if (!iterator->object->start) {
1659 zend_throw_error(NULL, "DatePeriod has not been initialized correctly");
1660 return;
1661 }
1662
1663 iterator->object->current = timelib_time_clone(iterator->object->start);
1664
1665 if (!iterator->object->include_start_date) {
1666 date_period_advance(iterator->object->current, iterator->object->interval);
1667 }
1668
1669 date_period_it_invalidate_current(iter);
1670 }
1671 /* }}} */
1672
1673 /* iterator handler table */
1674 static const zend_object_iterator_funcs date_period_it_funcs = {
1675 date_period_it_dtor,
1676 date_period_it_has_more,
1677 date_period_it_current_data,
1678 date_period_it_current_key,
1679 date_period_it_move_forward,
1680 date_period_it_rewind,
1681 date_period_it_invalidate_current,
1682 NULL, /* get_gc */
1683 };
1684
date_object_period_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1685 zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1686 {
1687 date_period_it *iterator;
1688
1689 if (by_ref) {
1690 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
1691 return NULL;
1692 }
1693
1694 iterator = emalloc(sizeof(date_period_it));
1695
1696 zend_iterator_init((zend_object_iterator*)iterator);
1697
1698 ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
1699 iterator->intern.funcs = &date_period_it_funcs;
1700 iterator->object = Z_PHPPERIOD_P(object);
1701 ZVAL_UNDEF(&iterator->current);
1702
1703 return (zend_object_iterator*)iterator;
1704 } /* }}} */
1705
implement_date_interface_handler(zend_class_entry * interface,zend_class_entry * implementor)1706 static int implement_date_interface_handler(zend_class_entry *interface, zend_class_entry *implementor) /* {{{ */
1707 {
1708 if (implementor->type == ZEND_USER_CLASS &&
1709 !instanceof_function(implementor, date_ce_date) &&
1710 !instanceof_function(implementor, date_ce_immutable)
1711 ) {
1712 zend_error(E_ERROR, "DateTimeInterface can't be implemented by user classes");
1713 }
1714
1715 return SUCCESS;
1716 } /* }}} */
1717
date_interval_has_property(zend_object * object,zend_string * name,int type,void ** cache_slot)1718 static int date_interval_has_property(zend_object *object, zend_string *name, int type, void **cache_slot) /* {{{ */
1719 {
1720 php_interval_obj *obj;
1721 zval rv;
1722 zval *prop;
1723 int retval = 0;
1724
1725 obj = php_interval_obj_from_obj(object);
1726
1727 if (!obj->initialized) {
1728 retval = zend_std_has_property(object, name, type, cache_slot);
1729 return retval;
1730 }
1731
1732 prop = date_interval_read_property(object, name, BP_VAR_IS, cache_slot, &rv);
1733
1734 if (prop != &EG(uninitialized_zval)) {
1735 if (type == 2) {
1736 retval = 1;
1737 } else if (type == 1) {
1738 retval = zend_is_true(prop);
1739 } else if (type == 0) {
1740 retval = (Z_TYPE_P(prop) != IS_NULL);
1741 }
1742 } else {
1743 retval = zend_std_has_property(object, name, type, cache_slot);
1744 }
1745
1746 return retval;
1747
1748 }
1749 /* }}} */
1750
date_register_classes(void)1751 static void date_register_classes(void) /* {{{ */
1752 {
1753 date_ce_interface = register_class_DateTimeInterface();
1754 date_ce_interface->interface_gets_implemented = implement_date_interface_handler;
1755
1756 date_ce_date = register_class_DateTime(date_ce_interface);
1757 date_ce_date->create_object = date_object_new_date;
1758 memcpy(&date_object_handlers_date, &std_object_handlers, sizeof(zend_object_handlers));
1759 date_object_handlers_date.offset = XtOffsetOf(php_date_obj, std);
1760 date_object_handlers_date.free_obj = date_object_free_storage_date;
1761 date_object_handlers_date.clone_obj = date_object_clone_date;
1762 date_object_handlers_date.compare = date_object_compare_date;
1763 date_object_handlers_date.get_properties_for = date_object_get_properties_for;
1764 date_object_handlers_date.get_gc = date_object_get_gc;
1765
1766 date_ce_immutable = register_class_DateTimeImmutable(date_ce_interface);
1767 date_ce_immutable->create_object = date_object_new_date;
1768 memcpy(&date_object_handlers_immutable, &std_object_handlers, sizeof(zend_object_handlers));
1769 date_object_handlers_immutable.clone_obj = date_object_clone_date;
1770 date_object_handlers_immutable.compare = date_object_compare_date;
1771 date_object_handlers_immutable.get_properties_for = date_object_get_properties_for;
1772 date_object_handlers_immutable.get_gc = date_object_get_gc;
1773
1774 date_ce_timezone = register_class_DateTimeZone();
1775 date_ce_timezone->create_object = date_object_new_timezone;
1776 memcpy(&date_object_handlers_timezone, &std_object_handlers, sizeof(zend_object_handlers));
1777 date_object_handlers_timezone.offset = XtOffsetOf(php_timezone_obj, std);
1778 date_object_handlers_timezone.free_obj = date_object_free_storage_timezone;
1779 date_object_handlers_timezone.clone_obj = date_object_clone_timezone;
1780 date_object_handlers_timezone.get_properties_for = date_object_get_properties_for_timezone;
1781 date_object_handlers_timezone.get_gc = date_object_get_gc_timezone;
1782 date_object_handlers_timezone.get_debug_info = date_object_get_debug_info_timezone;
1783 date_object_handlers_timezone.compare = date_object_compare_timezone;
1784
1785 date_ce_interval = register_class_DateInterval();
1786 date_ce_interval->create_object = date_object_new_interval;
1787 memcpy(&date_object_handlers_interval, &std_object_handlers, sizeof(zend_object_handlers));
1788 date_object_handlers_interval.offset = XtOffsetOf(php_interval_obj, std);
1789 date_object_handlers_interval.free_obj = date_object_free_storage_interval;
1790 date_object_handlers_interval.clone_obj = date_object_clone_interval;
1791 date_object_handlers_interval.has_property = date_interval_has_property;
1792 date_object_handlers_interval.read_property = date_interval_read_property;
1793 date_object_handlers_interval.write_property = date_interval_write_property;
1794 date_object_handlers_interval.get_properties = date_object_get_properties_interval;
1795 date_object_handlers_interval.get_property_ptr_ptr = date_interval_get_property_ptr_ptr;
1796 date_object_handlers_interval.get_gc = date_object_get_gc_interval;
1797 date_object_handlers_interval.compare = date_interval_compare_objects;
1798
1799 date_ce_period = register_class_DatePeriod(zend_ce_aggregate);
1800 date_ce_period->create_object = date_object_new_period;
1801 date_ce_period->get_iterator = date_object_period_get_iterator;
1802 memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
1803 date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);
1804 date_object_handlers_period.free_obj = date_object_free_storage_period;
1805 date_object_handlers_period.clone_obj = date_object_clone_period;
1806 date_object_handlers_period.get_gc = date_object_get_gc_period;
1807 date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
1808 date_object_handlers_period.read_property = date_period_read_property;
1809 date_object_handlers_period.write_property = date_period_write_property;
1810 } /* }}} */
1811
date_object_new_date(zend_class_entry * class_type)1812 static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */
1813 {
1814 php_date_obj *intern = zend_object_alloc(sizeof(php_date_obj), class_type);
1815
1816 zend_object_std_init(&intern->std, class_type);
1817 object_properties_init(&intern->std, class_type);
1818 intern->std.handlers = &date_object_handlers_date;
1819
1820 return &intern->std;
1821 } /* }}} */
1822
date_object_clone_date(zend_object * this_ptr)1823 static zend_object *date_object_clone_date(zend_object *this_ptr) /* {{{ */
1824 {
1825 php_date_obj *old_obj = php_date_obj_from_obj(this_ptr);
1826 php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(old_obj->std.ce));
1827
1828 zend_objects_clone_members(&new_obj->std, &old_obj->std);
1829 if (!old_obj->time) {
1830 return &new_obj->std;
1831 }
1832
1833 /* this should probably moved to a new `timelib_time *timelime_time_clone(timelib_time *)` */
1834 new_obj->time = timelib_time_ctor();
1835 *new_obj->time = *old_obj->time;
1836 if (old_obj->time->tz_abbr) {
1837 new_obj->time->tz_abbr = timelib_strdup(old_obj->time->tz_abbr);
1838 }
1839 if (old_obj->time->tz_info) {
1840 new_obj->time->tz_info = old_obj->time->tz_info;
1841 }
1842
1843 return &new_obj->std;
1844 } /* }}} */
1845
date_clone_immutable(zval * object,zval * new_object)1846 static void date_clone_immutable(zval *object, zval *new_object) /* {{{ */
1847 {
1848 ZVAL_OBJ(new_object, date_object_clone_date(Z_OBJ_P(object)));
1849 } /* }}} */
1850
date_object_compare_date(zval * d1,zval * d2)1851 static int date_object_compare_date(zval *d1, zval *d2) /* {{{ */
1852 {
1853 php_date_obj *o1;
1854 php_date_obj *o2;
1855
1856 ZEND_COMPARE_OBJECTS_FALLBACK(d1, d2);
1857
1858 o1 = Z_PHPDATE_P(d1);
1859 o2 = Z_PHPDATE_P(d2);
1860
1861 if (!o1->time || !o2->time) {
1862 php_error_docref(NULL, E_WARNING, "Trying to compare an incomplete DateTime or DateTimeImmutable object");
1863 return ZEND_UNCOMPARABLE;
1864 }
1865 if (!o1->time->sse_uptodate) {
1866 timelib_update_ts(o1->time, o1->time->tz_info);
1867 }
1868 if (!o2->time->sse_uptodate) {
1869 timelib_update_ts(o2->time, o2->time->tz_info);
1870 }
1871
1872 return timelib_time_compare(o1->time, o2->time);
1873 } /* }}} */
1874
date_object_get_gc(zend_object * object,zval ** table,int * n)1875 static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n) /* {{{ */
1876 {
1877 *table = NULL;
1878 *n = 0;
1879 return zend_std_get_properties(object);
1880 } /* }}} */
1881
date_object_get_gc_timezone(zend_object * object,zval ** table,int * n)1882 static HashTable *date_object_get_gc_timezone(zend_object *object, zval **table, int *n) /* {{{ */
1883 {
1884 *table = NULL;
1885 *n = 0;
1886 return zend_std_get_properties(object);
1887 } /* }}} */
1888
date_object_to_hash(php_date_obj * dateobj,HashTable * props)1889 static void date_object_to_hash(php_date_obj *dateobj, HashTable *props)
1890 {
1891 zval zv;
1892
1893 /* first we add the date and time in ISO format */
1894 ZVAL_STR(&zv, date_format("x-m-d H:i:s.u", sizeof("x-m-d H:i:s.u")-1, dateobj->time, 1));
1895 zend_hash_str_update(props, "date", sizeof("date")-1, &zv);
1896
1897 /* then we add the timezone name (or similar) */
1898 if (dateobj->time->is_localtime) {
1899 ZVAL_LONG(&zv, dateobj->time->zone_type);
1900 zend_hash_str_update(props, "timezone_type", sizeof("timezone_type")-1, &zv);
1901
1902 switch (dateobj->time->zone_type) {
1903 case TIMELIB_ZONETYPE_ID:
1904 ZVAL_STRING(&zv, dateobj->time->tz_info->name);
1905 break;
1906 case TIMELIB_ZONETYPE_OFFSET: {
1907 zend_string *tmpstr = zend_string_alloc(sizeof("UTC+05:00")-1, 0);
1908 int utc_offset = dateobj->time->z;
1909
1910 ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), sizeof("+05:00"), "%c%02d:%02d",
1911 utc_offset < 0 ? '-' : '+',
1912 abs(utc_offset / 3600),
1913 abs(((utc_offset % 3600) / 60)));
1914
1915 ZVAL_NEW_STR(&zv, tmpstr);
1916 }
1917 break;
1918 case TIMELIB_ZONETYPE_ABBR:
1919 ZVAL_STRING(&zv, dateobj->time->tz_abbr);
1920 break;
1921 }
1922 zend_hash_str_update(props, "timezone", sizeof("timezone")-1, &zv);
1923 }
1924 }
1925
date_object_get_properties_for(zend_object * object,zend_prop_purpose purpose)1926 static HashTable *date_object_get_properties_for(zend_object *object, zend_prop_purpose purpose) /* {{{ */
1927 {
1928 HashTable *props;
1929 php_date_obj *dateobj;
1930
1931 switch (purpose) {
1932 case ZEND_PROP_PURPOSE_DEBUG:
1933 case ZEND_PROP_PURPOSE_SERIALIZE:
1934 case ZEND_PROP_PURPOSE_VAR_EXPORT:
1935 case ZEND_PROP_PURPOSE_JSON:
1936 case ZEND_PROP_PURPOSE_ARRAY_CAST:
1937 break;
1938 default:
1939 return zend_std_get_properties_for(object, purpose);
1940 }
1941
1942 dateobj = php_date_obj_from_obj(object);
1943 props = zend_array_dup(zend_std_get_properties(object));
1944 if (!dateobj->time) {
1945 return props;
1946 }
1947
1948 date_object_to_hash(dateobj, props);
1949
1950 return props;
1951 } /* }}} */
1952
date_object_new_timezone(zend_class_entry * class_type)1953 static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{{ */
1954 {
1955 php_timezone_obj *intern = zend_object_alloc(sizeof(php_timezone_obj), class_type);
1956
1957 zend_object_std_init(&intern->std, class_type);
1958 object_properties_init(&intern->std, class_type);
1959 intern->std.handlers = &date_object_handlers_timezone;
1960
1961 return &intern->std;
1962 } /* }}} */
1963
date_object_clone_timezone(zend_object * this_ptr)1964 static zend_object *date_object_clone_timezone(zend_object *this_ptr) /* {{{ */
1965 {
1966 php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr);
1967 php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(old_obj->std.ce));
1968
1969 zend_objects_clone_members(&new_obj->std, &old_obj->std);
1970 if (!old_obj->initialized) {
1971 return &new_obj->std;
1972 }
1973
1974 new_obj->type = old_obj->type;
1975 new_obj->initialized = 1;
1976 switch (new_obj->type) {
1977 case TIMELIB_ZONETYPE_ID:
1978 new_obj->tzi.tz = old_obj->tzi.tz;
1979 break;
1980 case TIMELIB_ZONETYPE_OFFSET:
1981 new_obj->tzi.utc_offset = old_obj->tzi.utc_offset;
1982 break;
1983 case TIMELIB_ZONETYPE_ABBR:
1984 new_obj->tzi.z.utc_offset = old_obj->tzi.z.utc_offset;
1985 new_obj->tzi.z.dst = old_obj->tzi.z.dst;
1986 new_obj->tzi.z.abbr = timelib_strdup(old_obj->tzi.z.abbr);
1987 break;
1988 }
1989
1990 return &new_obj->std;
1991 } /* }}} */
1992
date_object_compare_timezone(zval * tz1,zval * tz2)1993 static int date_object_compare_timezone(zval *tz1, zval *tz2) /* {{{ */
1994 {
1995 php_timezone_obj *o1, *o2;
1996
1997 ZEND_COMPARE_OBJECTS_FALLBACK(tz1, tz2);
1998
1999 o1 = Z_PHPTIMEZONE_P(tz1);
2000 o2 = Z_PHPTIMEZONE_P(tz2);
2001
2002 if (!o1->initialized || !o2->initialized) {
2003 zend_throw_error(NULL, "Trying to compare uninitialized DateTimeZone objects");
2004 return 1;
2005 }
2006
2007 if (o1->type != o2->type) {
2008 php_error_docref(NULL, E_WARNING, "Trying to compare different kinds of DateTimeZone objects");
2009 return ZEND_UNCOMPARABLE;
2010 }
2011
2012 switch (o1->type) {
2013 case TIMELIB_ZONETYPE_OFFSET:
2014 return o1->tzi.utc_offset == o2->tzi.utc_offset ? 0 : 1;
2015 case TIMELIB_ZONETYPE_ABBR:
2016 return strcmp(o1->tzi.z.abbr, o2->tzi.z.abbr) ? 1 : 0;
2017 case TIMELIB_ZONETYPE_ID:
2018 return strcmp(o1->tzi.tz->name, o2->tzi.tz->name) ? 1 : 0;
2019 EMPTY_SWITCH_DEFAULT_CASE();
2020 }
2021 } /* }}} */
2022
php_timezone_to_string(php_timezone_obj * tzobj,zval * zv)2023 static void php_timezone_to_string(php_timezone_obj *tzobj, zval *zv)
2024 {
2025 switch (tzobj->type) {
2026 case TIMELIB_ZONETYPE_ID:
2027 ZVAL_STRING(zv, tzobj->tzi.tz->name);
2028 break;
2029 case TIMELIB_ZONETYPE_OFFSET: {
2030 timelib_sll utc_offset = tzobj->tzi.utc_offset;
2031 int seconds = utc_offset % 60;
2032 size_t size;
2033 const char *format;
2034 if (seconds == 0) {
2035 size = sizeof("+05:00");
2036 format = "%c%02d:%02d";
2037 } else {
2038 size = sizeof("+05:00:01");
2039 format = "%c%02d:%02d:%02d";
2040 }
2041 zend_string *tmpstr = zend_string_alloc(size - 1, 0);
2042
2043 /* Note: if seconds == 0, the seconds argument will be excessive and therefore ignored. */
2044 ZSTR_LEN(tmpstr) = snprintf(ZSTR_VAL(tmpstr), size, format,
2045 utc_offset < 0 ? '-' : '+',
2046 abs((int)(utc_offset / 3600)),
2047 abs((int)(utc_offset % 3600) / 60),
2048 abs(seconds));
2049
2050 ZVAL_NEW_STR(zv, tmpstr);
2051 }
2052 break;
2053 case TIMELIB_ZONETYPE_ABBR:
2054 ZVAL_STRING(zv, tzobj->tzi.z.abbr);
2055 break;
2056 }
2057 }
2058
date_timezone_object_to_hash(php_timezone_obj * tzobj,HashTable * props)2059 void date_timezone_object_to_hash(php_timezone_obj *tzobj, HashTable *props)
2060 {
2061 zval zv;
2062
2063 ZVAL_LONG(&zv, tzobj->type);
2064 zend_hash_str_update(props, "timezone_type", strlen("timezone_type"), &zv);
2065
2066 php_timezone_to_string(tzobj, &zv);
2067 zend_hash_str_update(props, "timezone", strlen("timezone"), &zv);
2068 }
2069
date_object_get_properties_for_timezone(zend_object * object,zend_prop_purpose purpose)2070 static HashTable *date_object_get_properties_for_timezone(zend_object *object, zend_prop_purpose purpose) /* {{{ */
2071 {
2072 HashTable *props;
2073 php_timezone_obj *tzobj;
2074
2075 switch (purpose) {
2076 case ZEND_PROP_PURPOSE_DEBUG:
2077 case ZEND_PROP_PURPOSE_SERIALIZE:
2078 case ZEND_PROP_PURPOSE_VAR_EXPORT:
2079 case ZEND_PROP_PURPOSE_JSON:
2080 case ZEND_PROP_PURPOSE_ARRAY_CAST:
2081 break;
2082 default:
2083 return zend_std_get_properties_for(object, purpose);
2084 }
2085
2086 tzobj = php_timezone_obj_from_obj(object);
2087 props = zend_array_dup(zend_std_get_properties(object));
2088 if (!tzobj->initialized) {
2089 return props;
2090 }
2091
2092 date_timezone_object_to_hash(tzobj, props);
2093
2094 return props;
2095 } /* }}} */
2096
date_object_get_debug_info_timezone(zend_object * object,int * is_temp)2097 static HashTable *date_object_get_debug_info_timezone(zend_object *object, int *is_temp) /* {{{ */
2098 {
2099 HashTable *ht, *props;
2100 zval zv;
2101 php_timezone_obj *tzobj;
2102
2103 tzobj = php_timezone_obj_from_obj(object);
2104 props = zend_std_get_properties(object);
2105
2106 *is_temp = 1;
2107 ht = zend_array_dup(props);
2108
2109 ZVAL_LONG(&zv, tzobj->type);
2110 zend_hash_str_update(ht, "timezone_type", sizeof("timezone_type")-1, &zv);
2111
2112 php_timezone_to_string(tzobj, &zv);
2113 zend_hash_str_update(ht, "timezone", sizeof("timezone")-1, &zv);
2114
2115 return ht;
2116 } /* }}} */
2117
date_object_new_interval(zend_class_entry * class_type)2118 static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{{ */
2119 {
2120 php_interval_obj *intern = zend_object_alloc(sizeof(php_interval_obj), class_type);
2121
2122 zend_object_std_init(&intern->std, class_type);
2123 object_properties_init(&intern->std, class_type);
2124 intern->std.handlers = &date_object_handlers_interval;
2125
2126 return &intern->std;
2127 } /* }}} */
2128
date_object_clone_interval(zend_object * this_ptr)2129 static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */
2130 {
2131 php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr);
2132 php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce));
2133
2134 zend_objects_clone_members(&new_obj->std, &old_obj->std);
2135 new_obj->civil_or_wall = old_obj->civil_or_wall;
2136 new_obj->from_string = old_obj->from_string;
2137 if (old_obj->date_string) {
2138 new_obj->date_string = zend_string_copy(old_obj->date_string);
2139 }
2140 new_obj->initialized = old_obj->initialized;
2141 if (old_obj->diff) {
2142 new_obj->diff = timelib_rel_time_clone(old_obj->diff);
2143 }
2144
2145 return &new_obj->std;
2146 } /* }}} */
2147
date_object_get_gc_interval(zend_object * object,zval ** table,int * n)2148 static HashTable *date_object_get_gc_interval(zend_object *object, zval **table, int *n) /* {{{ */
2149 {
2150
2151 *table = NULL;
2152 *n = 0;
2153 return zend_std_get_properties(object);
2154 } /* }}} */
2155
date_interval_object_to_hash(php_interval_obj * intervalobj,HashTable * props)2156 static void date_interval_object_to_hash(php_interval_obj *intervalobj, HashTable *props)
2157 {
2158 zval zv;
2159
2160 /* Records whether this is a special relative interval that needs to be recreated from a string */
2161 if (intervalobj->from_string) {
2162 ZVAL_BOOL(&zv, (zend_bool)intervalobj->from_string);
2163 zend_hash_str_update(props, "from_string", strlen("from_string"), &zv);
2164 ZVAL_STR_COPY(&zv, intervalobj->date_string);
2165 zend_hash_str_update(props, "date_string", strlen("date_string"), &zv);
2166 return;
2167 }
2168
2169 #define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
2170 ZVAL_LONG(&zv, (zend_long)intervalobj->diff->f); \
2171 zend_hash_str_update(props, n, sizeof(n)-1, &zv);
2172
2173 PHP_DATE_INTERVAL_ADD_PROPERTY("y", y);
2174 PHP_DATE_INTERVAL_ADD_PROPERTY("m", m);
2175 PHP_DATE_INTERVAL_ADD_PROPERTY("d", d);
2176 PHP_DATE_INTERVAL_ADD_PROPERTY("h", h);
2177 PHP_DATE_INTERVAL_ADD_PROPERTY("i", i);
2178 PHP_DATE_INTERVAL_ADD_PROPERTY("s", s);
2179 ZVAL_DOUBLE(&zv, (double)intervalobj->diff->us / 1000000.0);
2180 zend_hash_str_update(props, "f", sizeof("f") - 1, &zv);
2181 PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert);
2182 if (intervalobj->diff->days != TIMELIB_UNSET) {
2183 PHP_DATE_INTERVAL_ADD_PROPERTY("days", days);
2184 } else {
2185 ZVAL_FALSE(&zv);
2186 zend_hash_str_update(props, "days", sizeof("days")-1, &zv);
2187 }
2188 ZVAL_BOOL(&zv, (zend_bool)intervalobj->from_string);
2189 zend_hash_str_update(props, "from_string", strlen("from_string"), &zv);
2190
2191 #undef PHP_DATE_INTERVAL_ADD_PROPERTY
2192 }
2193
date_object_get_properties_interval(zend_object * object)2194 static HashTable *date_object_get_properties_interval(zend_object *object) /* {{{ */
2195 {
2196 HashTable *props;
2197 php_interval_obj *intervalobj;
2198
2199 intervalobj = php_interval_obj_from_obj(object);
2200 props = zend_std_get_properties(object);
2201 if (!intervalobj->initialized) {
2202 return props;
2203 }
2204
2205 date_interval_object_to_hash(intervalobj, props);
2206
2207 return props;
2208 } /* }}} */
2209
date_object_new_period(zend_class_entry * class_type)2210 static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ */
2211 {
2212 php_period_obj *intern = zend_object_alloc(sizeof(php_period_obj), class_type);
2213
2214 zend_object_std_init(&intern->std, class_type);
2215 object_properties_init(&intern->std, class_type);
2216
2217 intern->std.handlers = &date_object_handlers_period;
2218
2219 return &intern->std;
2220 } /* }}} */
2221
date_object_clone_period(zend_object * this_ptr)2222 static zend_object *date_object_clone_period(zend_object *this_ptr) /* {{{ */
2223 {
2224 php_period_obj *old_obj = php_period_obj_from_obj(this_ptr);
2225 php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(old_obj->std.ce));
2226
2227 zend_objects_clone_members(&new_obj->std, &old_obj->std);
2228 new_obj->initialized = old_obj->initialized;
2229 new_obj->recurrences = old_obj->recurrences;
2230 new_obj->include_start_date = old_obj->include_start_date;
2231 new_obj->include_end_date = old_obj->include_end_date;
2232 new_obj->start_ce = old_obj->start_ce;
2233
2234 if (old_obj->start) {
2235 new_obj->start = timelib_time_clone(old_obj->start);
2236 }
2237 if (old_obj->current) {
2238 new_obj->current = timelib_time_clone(old_obj->current);
2239 }
2240 if (old_obj->end) {
2241 new_obj->end = timelib_time_clone(old_obj->end);
2242 }
2243 if (old_obj->interval) {
2244 new_obj->interval = timelib_rel_time_clone(old_obj->interval);
2245 }
2246 return &new_obj->std;
2247 } /* }}} */
2248
date_object_free_storage_date(zend_object * object)2249 static void date_object_free_storage_date(zend_object *object) /* {{{ */
2250 {
2251 php_date_obj *intern = php_date_obj_from_obj(object);
2252
2253 if (intern->time) {
2254 timelib_time_dtor(intern->time);
2255 }
2256
2257 zend_object_std_dtor(&intern->std);
2258 } /* }}} */
2259
date_object_free_storage_timezone(zend_object * object)2260 static void date_object_free_storage_timezone(zend_object *object) /* {{{ */
2261 {
2262 php_timezone_obj *intern = php_timezone_obj_from_obj(object);
2263
2264 if (intern->type == TIMELIB_ZONETYPE_ABBR) {
2265 timelib_free(intern->tzi.z.abbr);
2266 }
2267 zend_object_std_dtor(&intern->std);
2268 } /* }}} */
2269
date_object_free_storage_interval(zend_object * object)2270 static void date_object_free_storage_interval(zend_object *object) /* {{{ */
2271 {
2272 php_interval_obj *intern = php_interval_obj_from_obj(object);
2273
2274 if (intern->date_string) {
2275 zend_string_release(intern->date_string);
2276 intern->date_string = NULL;
2277 }
2278 timelib_rel_time_dtor(intern->diff);
2279 zend_object_std_dtor(&intern->std);
2280 } /* }}} */
2281
date_object_free_storage_period(zend_object * object)2282 static void date_object_free_storage_period(zend_object *object) /* {{{ */
2283 {
2284 php_period_obj *intern = php_period_obj_from_obj(object);
2285
2286 if (intern->start) {
2287 timelib_time_dtor(intern->start);
2288 }
2289
2290 if (intern->current) {
2291 timelib_time_dtor(intern->current);
2292 }
2293
2294 if (intern->end) {
2295 timelib_time_dtor(intern->end);
2296 }
2297
2298 timelib_rel_time_dtor(intern->interval);
2299 zend_object_std_dtor(&intern->std);
2300 } /* }}} */
2301
add_common_properties(HashTable * myht,zend_object * zobj)2302 static void add_common_properties(HashTable *myht, zend_object *zobj)
2303 {
2304 HashTable *common;
2305 zend_string *name;
2306 zval *prop;
2307
2308 common = zend_std_get_properties(zobj);
2309
2310 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_IND(common, name, prop) {
2311 if (zend_hash_add(myht, name, prop) != NULL) {
2312 Z_TRY_ADDREF_P(prop);
2313 }
2314 } ZEND_HASH_FOREACH_END();
2315 }
2316
2317 /* Advanced Interface */
php_date_instantiate(zend_class_entry * pce,zval * object)2318 PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object) /* {{{ */
2319 {
2320 object_init_ex(object, pce);
2321 return object;
2322 } /* }}} */
2323
2324 /* Helper function used to store the latest found warnings and errors while
2325 * parsing, from either strtotime or parse_from_format. */
update_errors_warnings(timelib_error_container ** last_errors)2326 static void update_errors_warnings(timelib_error_container **last_errors) /* {{{ */
2327 {
2328 if (DATEG(last_errors)) {
2329 timelib_error_container_dtor(DATEG(last_errors));
2330 DATEG(last_errors) = NULL;
2331 }
2332
2333 if (last_errors == NULL || (*last_errors) == NULL) {
2334 return;
2335 }
2336
2337 if ((*last_errors)->warning_count || (*last_errors)->error_count) {
2338 DATEG(last_errors) = *last_errors;
2339 return;
2340 }
2341
2342 timelib_error_container_dtor(*last_errors);
2343 *last_errors = NULL;
2344 } /* }}} */
2345
php_date_set_time_fraction(timelib_time * time,int microseconds)2346 static void php_date_set_time_fraction(timelib_time *time, int microseconds)
2347 {
2348 time->us = microseconds;
2349 }
2350
php_date_get_current_time_with_fraction(time_t * sec,suseconds_t * usec)2351 static void php_date_get_current_time_with_fraction(time_t *sec, suseconds_t *usec)
2352 {
2353 #if HAVE_GETTIMEOFDAY
2354 struct timeval tp = {0}; /* For setting microseconds */
2355
2356 gettimeofday(&tp, NULL);
2357 *sec = tp.tv_sec;
2358 *usec = tp.tv_usec;
2359 #else
2360 *sec = time(NULL);
2361 *usec = 0;
2362 #endif
2363 }
2364
php_date_initialize(php_date_obj * dateobj,const char * time_str,size_t time_str_len,const char * format,zval * timezone_object,int flags)2365 PHPAPI bool php_date_initialize(php_date_obj *dateobj, const char *time_str, size_t time_str_len, const char *format, zval *timezone_object, int flags) /* {{{ */
2366 {
2367 timelib_time *now;
2368 timelib_tzinfo *tzi = NULL;
2369 timelib_error_container *err = NULL;
2370 int type = TIMELIB_ZONETYPE_ID, new_dst = 0;
2371 char *new_abbr = NULL;
2372 timelib_sll new_offset = 0;
2373 time_t sec;
2374 suseconds_t usec;
2375 int options = 0;
2376
2377 if (dateobj->time) {
2378 timelib_time_dtor(dateobj->time);
2379 }
2380 if (format) {
2381 if (time_str_len == 0) {
2382 time_str = "";
2383 }
2384 dateobj->time = timelib_parse_from_format(format, time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2385 } else {
2386 if (time_str_len == 0) {
2387 time_str = "now";
2388 time_str_len = sizeof("now") - 1;
2389 }
2390 dateobj->time = timelib_strtotime(time_str, time_str_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
2391 }
2392
2393 /* update last errors and warnings */
2394 update_errors_warnings(&err);
2395
2396 /* If called from a constructor throw an exception */
2397 if ((flags & PHP_DATE_INIT_CTOR) && err && err->error_count) {
2398 /* spit out the first library error message, at least */
2399 zend_throw_exception_ex(NULL, 0, "Failed to parse time string (%s) at position %d (%c): %s", time_str,
2400 err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
2401 }
2402 if (err && err->error_count) {
2403 timelib_time_dtor(dateobj->time);
2404 dateobj->time = 0;
2405 return 0;
2406 }
2407
2408 if (timezone_object) {
2409 php_timezone_obj *tzobj;
2410
2411 tzobj = Z_PHPTIMEZONE_P(timezone_object);
2412 switch (tzobj->type) {
2413 case TIMELIB_ZONETYPE_ID:
2414 tzi = tzobj->tzi.tz;
2415 break;
2416 case TIMELIB_ZONETYPE_OFFSET:
2417 new_offset = tzobj->tzi.utc_offset;
2418 break;
2419 case TIMELIB_ZONETYPE_ABBR:
2420 new_offset = tzobj->tzi.z.utc_offset;
2421 new_dst = tzobj->tzi.z.dst;
2422 new_abbr = timelib_strdup(tzobj->tzi.z.abbr);
2423 break;
2424 default:
2425 zend_throw_error(NULL, "The DateTimeZone object has not been correctly initialized by its constructor");
2426 return 0;
2427 }
2428 type = tzobj->type;
2429 } else if (dateobj->time->tz_info) {
2430 tzi = dateobj->time->tz_info;
2431 } else {
2432 tzi = get_timezone_info();
2433 if (!tzi) {
2434 return 0;
2435 }
2436 }
2437
2438 now = timelib_time_ctor();
2439 now->zone_type = type;
2440 switch (type) {
2441 case TIMELIB_ZONETYPE_ID:
2442 now->tz_info = tzi;
2443 break;
2444 case TIMELIB_ZONETYPE_OFFSET:
2445 now->z = new_offset;
2446 break;
2447 case TIMELIB_ZONETYPE_ABBR:
2448 now->z = new_offset;
2449 now->dst = new_dst;
2450 now->tz_abbr = new_abbr;
2451 break;
2452 }
2453 php_date_get_current_time_with_fraction(&sec, &usec);
2454 timelib_unixtime2local(now, (timelib_sll) sec);
2455 php_date_set_time_fraction(now, usec);
2456
2457 if (!format
2458 && time_str_len == sizeof("now") - 1
2459 && memcmp(time_str, "now", sizeof("now") - 1) == 0) {
2460 timelib_time_dtor(dateobj->time);
2461 dateobj->time = now;
2462 return 1;
2463 }
2464
2465 options = TIMELIB_NO_CLONE;
2466 if (flags & PHP_DATE_INIT_FORMAT) {
2467 options |= TIMELIB_OVERRIDE_TIME;
2468 }
2469 timelib_fill_holes(dateobj->time, now, options);
2470
2471 timelib_update_ts(dateobj->time, tzi);
2472 timelib_update_from_sse(dateobj->time);
2473
2474 dateobj->time->have_relative = 0;
2475
2476 timelib_time_dtor(now);
2477
2478 return 1;
2479 } /* }}} */
2480
2481 /* {{{ Returns new DateTime object */
PHP_FUNCTION(date_create)2482 PHP_FUNCTION(date_create)
2483 {
2484 zval *timezone_object = NULL;
2485 char *time_str = NULL;
2486 size_t time_str_len = 0;
2487
2488 ZEND_PARSE_PARAMETERS_START(0, 2)
2489 Z_PARAM_OPTIONAL
2490 Z_PARAM_STRING(time_str, time_str_len)
2491 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2492 ZEND_PARSE_PARAMETERS_END();
2493
2494 php_date_instantiate(date_ce_date, return_value);
2495 if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2496 zval_ptr_dtor(return_value);
2497 RETURN_FALSE;
2498 }
2499 }
2500 /* }}} */
2501
2502 /* {{{ Returns new DateTimeImmutable object */
PHP_FUNCTION(date_create_immutable)2503 PHP_FUNCTION(date_create_immutable)
2504 {
2505 zval *timezone_object = NULL;
2506 char *time_str = NULL;
2507 size_t time_str_len = 0;
2508
2509 ZEND_PARSE_PARAMETERS_START(0, 2)
2510 Z_PARAM_OPTIONAL
2511 Z_PARAM_STRING(time_str, time_str_len)
2512 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2513 ZEND_PARSE_PARAMETERS_END();
2514
2515 php_date_instantiate(date_ce_immutable, return_value);
2516 if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, NULL, timezone_object, 0)) {
2517 zval_ptr_dtor(return_value);
2518 RETURN_FALSE;
2519 }
2520 }
2521 /* }}} */
2522
2523 /* {{{ Returns new DateTime object formatted according to the specified format */
PHP_FUNCTION(date_create_from_format)2524 PHP_FUNCTION(date_create_from_format)
2525 {
2526 zval *timezone_object = NULL;
2527 char *time_str = NULL, *format_str = NULL;
2528 size_t time_str_len = 0, format_str_len = 0;
2529
2530 ZEND_PARSE_PARAMETERS_START(2, 3)
2531 Z_PARAM_STRING(format_str, format_str_len)
2532 Z_PARAM_PATH(time_str, time_str_len)
2533 Z_PARAM_OPTIONAL
2534 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2535 ZEND_PARSE_PARAMETERS_END();
2536
2537 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2538 if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) {
2539 zval_ptr_dtor(return_value);
2540 RETURN_FALSE;
2541 }
2542 }
2543 /* }}} */
2544
2545 /* {{{ Returns new DateTime object formatted according to the specified format */
PHP_FUNCTION(date_create_immutable_from_format)2546 PHP_FUNCTION(date_create_immutable_from_format)
2547 {
2548 zval *timezone_object = NULL;
2549 char *time_str = NULL, *format_str = NULL;
2550 size_t time_str_len = 0, format_str_len = 0;
2551
2552 ZEND_PARSE_PARAMETERS_START(2, 3)
2553 Z_PARAM_STRING(format_str, format_str_len)
2554 Z_PARAM_PATH(time_str, time_str_len)
2555 Z_PARAM_OPTIONAL
2556 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2557 ZEND_PARSE_PARAMETERS_END();
2558
2559 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2560 if (!php_date_initialize(Z_PHPDATE_P(return_value), time_str, time_str_len, format_str, timezone_object, PHP_DATE_INIT_FORMAT)) {
2561 zval_ptr_dtor(return_value);
2562 RETURN_FALSE;
2563 }
2564 }
2565 /* }}} */
2566
2567 /* {{{ Creates new DateTime object */
PHP_METHOD(DateTime,__construct)2568 PHP_METHOD(DateTime, __construct)
2569 {
2570 zval *timezone_object = NULL;
2571 char *time_str = NULL;
2572 size_t time_str_len = 0;
2573
2574 ZEND_PARSE_PARAMETERS_START(0, 2)
2575 Z_PARAM_OPTIONAL
2576 Z_PARAM_STRING(time_str, time_str_len)
2577 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2578 ZEND_PARSE_PARAMETERS_END();
2579
2580 php_date_initialize(Z_PHPDATE_P(ZEND_THIS), time_str, time_str_len, NULL, timezone_object, PHP_DATE_INIT_CTOR);
2581 }
2582 /* }}} */
2583
2584 /* {{{ Creates new DateTimeImmutable object */
PHP_METHOD(DateTimeImmutable,__construct)2585 PHP_METHOD(DateTimeImmutable, __construct)
2586 {
2587 zval *timezone_object = NULL;
2588 char *time_str = NULL;
2589 size_t time_str_len = 0;
2590
2591 ZEND_PARSE_PARAMETERS_START(0, 2)
2592 Z_PARAM_OPTIONAL
2593 Z_PARAM_STRING(time_str, time_str_len)
2594 Z_PARAM_OBJECT_OF_CLASS_OR_NULL(timezone_object, date_ce_timezone)
2595 ZEND_PARSE_PARAMETERS_END();
2596
2597 php_date_initialize(Z_PHPDATE_P(ZEND_THIS), time_str, time_str_len, NULL, timezone_object, PHP_DATE_INIT_CTOR);
2598 }
2599 /* }}} */
2600
2601 /* {{{ Creates new DateTime object from an existing immutable DateTimeImmutable object. */
PHP_METHOD(DateTime,createFromImmutable)2602 PHP_METHOD(DateTime, createFromImmutable)
2603 {
2604 zval *datetimeimmutable_object = NULL;
2605 php_date_obj *new_obj = NULL;
2606 php_date_obj *old_obj = NULL;
2607
2608 ZEND_PARSE_PARAMETERS_START(1, 1)
2609 Z_PARAM_OBJECT_OF_CLASS(datetimeimmutable_object, date_ce_immutable)
2610 ZEND_PARSE_PARAMETERS_END();
2611
2612 old_obj = Z_PHPDATE_P(datetimeimmutable_object);
2613 DATE_CHECK_INITIALIZED(old_obj->time, DateTimeImmutable);
2614
2615 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2616 new_obj = Z_PHPDATE_P(return_value);
2617
2618 new_obj->time = timelib_time_clone(old_obj->time);
2619 }
2620 /* }}} */
2621
2622 /* {{{ Creates new DateTime object from an existing DateTimeInterface object. */
PHP_METHOD(DateTime,createFromInterface)2623 PHP_METHOD(DateTime, createFromInterface)
2624 {
2625 zval *datetimeinterface_object = NULL;
2626 php_date_obj *new_obj = NULL;
2627 php_date_obj *old_obj = NULL;
2628
2629 ZEND_PARSE_PARAMETERS_START(1, 1)
2630 Z_PARAM_OBJECT_OF_CLASS(datetimeinterface_object, date_ce_interface)
2631 ZEND_PARSE_PARAMETERS_END();
2632
2633 old_obj = Z_PHPDATE_P(datetimeinterface_object);
2634 DATE_CHECK_INITIALIZED(old_obj->time, DateTimeInterface);
2635
2636 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_date, return_value);
2637 new_obj = Z_PHPDATE_P(return_value);
2638
2639 new_obj->time = timelib_time_clone(old_obj->time);
2640 }
2641 /* }}} */
2642
2643 /* {{{ Creates new DateTimeImmutable object from an existing mutable DateTime object. */
PHP_METHOD(DateTimeImmutable,createFromMutable)2644 PHP_METHOD(DateTimeImmutable, createFromMutable)
2645 {
2646 zval *datetime_object = NULL;
2647 php_date_obj *new_obj = NULL;
2648 php_date_obj *old_obj = NULL;
2649
2650 ZEND_PARSE_PARAMETERS_START(1, 1)
2651 Z_PARAM_OBJECT_OF_CLASS(datetime_object, date_ce_date)
2652 ZEND_PARSE_PARAMETERS_END();
2653
2654 old_obj = Z_PHPDATE_P(datetime_object);
2655 DATE_CHECK_INITIALIZED(old_obj->time, DateTime);
2656
2657 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2658 new_obj = Z_PHPDATE_P(return_value);
2659
2660 new_obj->time = timelib_time_clone(old_obj->time);
2661 }
2662 /* }}} */
2663
2664 /* {{{ Creates new DateTimeImmutable object from an existing DateTimeInterface object. */
PHP_METHOD(DateTimeImmutable,createFromInterface)2665 PHP_METHOD(DateTimeImmutable, createFromInterface)
2666 {
2667 zval *datetimeinterface_object = NULL;
2668 php_date_obj *new_obj = NULL;
2669 php_date_obj *old_obj = NULL;
2670
2671 ZEND_PARSE_PARAMETERS_START(1, 1)
2672 Z_PARAM_OBJECT_OF_CLASS(datetimeinterface_object, date_ce_interface)
2673 ZEND_PARSE_PARAMETERS_END();
2674
2675 old_obj = Z_PHPDATE_P(datetimeinterface_object);
2676 DATE_CHECK_INITIALIZED(old_obj->time, DateTimeInterface);
2677
2678 php_date_instantiate(execute_data->This.value.ce ? execute_data->This.value.ce : date_ce_immutable, return_value);
2679 new_obj = Z_PHPDATE_P(return_value);
2680
2681 new_obj->time = timelib_time_clone(old_obj->time);
2682 }
2683 /* }}} */
2684
php_date_initialize_from_hash(php_date_obj ** dateobj,HashTable * myht)2685 static bool php_date_initialize_from_hash(php_date_obj **dateobj, HashTable *myht)
2686 {
2687 zval *z_date;
2688 zval *z_timezone_type;
2689 zval *z_timezone;
2690 zval tmp_obj;
2691 timelib_tzinfo *tzi;
2692
2693 z_date = zend_hash_str_find(myht, "date", sizeof("date")-1);
2694 if (!z_date || Z_TYPE_P(z_date) != IS_STRING) {
2695 return false;
2696 }
2697
2698 z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type")-1);
2699 if (!z_timezone_type || Z_TYPE_P(z_timezone_type) != IS_LONG) {
2700 return false;
2701 }
2702
2703 z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone")-1);
2704 if (!z_timezone || Z_TYPE_P(z_timezone) != IS_STRING) {
2705 return false;
2706 }
2707
2708 switch (Z_LVAL_P(z_timezone_type)) {
2709 case TIMELIB_ZONETYPE_OFFSET:
2710 case TIMELIB_ZONETYPE_ABBR: {
2711 zend_string *tmp = zend_string_concat3(
2712 Z_STRVAL_P(z_date), Z_STRLEN_P(z_date), " ", 1,
2713 Z_STRVAL_P(z_timezone), Z_STRLEN_P(z_timezone));
2714 bool ret = php_date_initialize(*dateobj, ZSTR_VAL(tmp), ZSTR_LEN(tmp), NULL, NULL, 0);
2715 zend_string_release(tmp);
2716 return ret;
2717 }
2718
2719 case TIMELIB_ZONETYPE_ID: {
2720 bool ret;
2721 php_timezone_obj *tzobj;
2722
2723 tzi = php_date_parse_tzfile(Z_STRVAL_P(z_timezone), DATE_TIMEZONEDB);
2724
2725 if (tzi == NULL) {
2726 return false;
2727 }
2728
2729 tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, &tmp_obj));
2730 tzobj->type = TIMELIB_ZONETYPE_ID;
2731 tzobj->tzi.tz = tzi;
2732 tzobj->initialized = 1;
2733
2734 ret = php_date_initialize(*dateobj, Z_STRVAL_P(z_date), Z_STRLEN_P(z_date), NULL, &tmp_obj, 0);
2735 zval_ptr_dtor(&tmp_obj);
2736 return ret;
2737 }
2738 }
2739 return false;
2740 } /* }}} */
2741
2742 /* {{{ */
PHP_METHOD(DateTime,__set_state)2743 PHP_METHOD(DateTime, __set_state)
2744 {
2745 php_date_obj *dateobj;
2746 zval *array;
2747 HashTable *myht;
2748
2749 ZEND_PARSE_PARAMETERS_START(1, 1)
2750 Z_PARAM_ARRAY(array)
2751 ZEND_PARSE_PARAMETERS_END();
2752
2753 myht = Z_ARRVAL_P(array);
2754
2755 php_date_instantiate(date_ce_date, return_value);
2756 dateobj = Z_PHPDATE_P(return_value);
2757 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2758 zend_throw_error(NULL, "Invalid serialization data for DateTime object");
2759 RETURN_THROWS();
2760 }
2761 }
2762 /* }}} */
2763
2764 /* {{{ */
PHP_METHOD(DateTimeImmutable,__set_state)2765 PHP_METHOD(DateTimeImmutable, __set_state)
2766 {
2767 php_date_obj *dateobj;
2768 zval *array;
2769 HashTable *myht;
2770
2771 ZEND_PARSE_PARAMETERS_START(1, 1)
2772 Z_PARAM_ARRAY(array)
2773 ZEND_PARSE_PARAMETERS_END();
2774
2775 myht = Z_ARRVAL_P(array);
2776
2777 php_date_instantiate(date_ce_immutable, return_value);
2778 dateobj = Z_PHPDATE_P(return_value);
2779 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2780 zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
2781 RETURN_THROWS();
2782 }
2783 }
2784 /* }}} */
2785
2786 /* {{{ */
PHP_METHOD(DateTime,__serialize)2787 PHP_METHOD(DateTime, __serialize)
2788 {
2789 zval *object = ZEND_THIS;
2790 php_date_obj *dateobj;
2791 HashTable *myht;
2792
2793 ZEND_PARSE_PARAMETERS_NONE();
2794
2795 dateobj = Z_PHPDATE_P(object);
2796 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
2797
2798 array_init(return_value);
2799 myht = Z_ARRVAL_P(return_value);
2800 date_object_to_hash(dateobj, myht);
2801
2802 add_common_properties(myht, &dateobj->std);
2803 }
2804 /* }}} */
2805
2806 /* {{{ */
PHP_METHOD(DateTimeImmutable,__serialize)2807 PHP_METHOD(DateTimeImmutable, __serialize)
2808 {
2809 zval *object = ZEND_THIS;
2810 php_date_obj *dateobj;
2811 HashTable *myht;
2812
2813 ZEND_PARSE_PARAMETERS_NONE();
2814
2815 dateobj = Z_PHPDATE_P(object);
2816 DATE_CHECK_INITIALIZED(dateobj->time, DateTimeImmutable);
2817
2818 array_init(return_value);
2819 myht = Z_ARRVAL_P(return_value);
2820 date_object_to_hash(dateobj, myht);
2821
2822 add_common_properties(myht, &dateobj->std);
2823 }
2824 /* }}} */
2825
date_time_is_internal_property(zend_string * name)2826 static bool date_time_is_internal_property(zend_string *name)
2827 {
2828 if (
2829 zend_string_equals_literal(name, "date") ||
2830 zend_string_equals_literal(name, "timezone_type") ||
2831 zend_string_equals_literal(name, "timezone")
2832 ) {
2833 return 1;
2834 }
2835 return 0;
2836 }
2837
restore_custom_datetime_properties(zval * object,HashTable * myht)2838 static void restore_custom_datetime_properties(zval *object, HashTable *myht)
2839 {
2840 zend_string *prop_name;
2841 zval *prop_val;
2842
2843 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
2844 if (!prop_name || (Z_TYPE_P(prop_val) == IS_REFERENCE) || date_time_is_internal_property(prop_name)) {
2845 continue;
2846 }
2847 update_property(Z_OBJ_P(object), prop_name, prop_val);
2848 } ZEND_HASH_FOREACH_END();
2849 }
2850
2851 /* {{{ */
PHP_METHOD(DateTime,__unserialize)2852 PHP_METHOD(DateTime, __unserialize)
2853 {
2854 zval *object = ZEND_THIS;
2855 php_date_obj *dateobj;
2856 zval *array;
2857 HashTable *myht;
2858
2859 ZEND_PARSE_PARAMETERS_START(1, 1)
2860 Z_PARAM_ARRAY(array)
2861 ZEND_PARSE_PARAMETERS_END();
2862
2863 dateobj = Z_PHPDATE_P(object);
2864 myht = Z_ARRVAL_P(array);
2865
2866 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2867 zend_throw_error(NULL, "Invalid serialization data for DateTime object");
2868 RETURN_THROWS();
2869 }
2870
2871 restore_custom_datetime_properties(object, myht);
2872 }
2873 /* }}} */
2874
2875 /* {{{ */
PHP_METHOD(DateTimeImmutable,__unserialize)2876 PHP_METHOD(DateTimeImmutable, __unserialize)
2877 {
2878 zval *object = ZEND_THIS;
2879 php_date_obj *dateobj;
2880 zval *array;
2881 HashTable *myht;
2882
2883 ZEND_PARSE_PARAMETERS_START(1, 1)
2884 Z_PARAM_ARRAY(array)
2885 ZEND_PARSE_PARAMETERS_END();
2886
2887 dateobj = Z_PHPDATE_P(object);
2888 myht = Z_ARRVAL_P(array);
2889
2890 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2891 zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
2892 RETURN_THROWS();
2893 }
2894
2895 restore_custom_datetime_properties(object, myht);
2896 }
2897 /* }}} */
2898
2899 /* {{{ */
PHP_METHOD(DateTime,__wakeup)2900 PHP_METHOD(DateTime, __wakeup)
2901 {
2902 zval *object = ZEND_THIS;
2903 php_date_obj *dateobj;
2904 HashTable *myht;
2905
2906 ZEND_PARSE_PARAMETERS_NONE();
2907
2908 dateobj = Z_PHPDATE_P(object);
2909
2910 myht = Z_OBJPROP_P(object);
2911
2912 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2913 zend_throw_error(NULL, "Invalid serialization data for DateTime object");
2914 RETURN_THROWS();
2915 }
2916 }
2917 /* }}} */
2918
2919 /* {{{ */
PHP_METHOD(DateTimeImmutable,__wakeup)2920 PHP_METHOD(DateTimeImmutable, __wakeup)
2921 {
2922 zval *object = ZEND_THIS;
2923 php_date_obj *dateobj;
2924 HashTable *myht;
2925
2926 ZEND_PARSE_PARAMETERS_NONE();
2927
2928 dateobj = Z_PHPDATE_P(object);
2929
2930 myht = Z_OBJPROP_P(object);
2931
2932 if (!php_date_initialize_from_hash(&dateobj, myht)) {
2933 zend_throw_error(NULL, "Invalid serialization data for DateTimeImmutable object");
2934 RETURN_THROWS();
2935 }
2936 }
2937 /* }}} */
2938
2939 /* Helper function used to add an associative array of warnings and errors to a zval */
zval_from_error_container(zval * z,timelib_error_container * error)2940 static void zval_from_error_container(zval *z, timelib_error_container *error) /* {{{ */
2941 {
2942 int i;
2943 zval element;
2944
2945 add_assoc_long(z, "warning_count", error->warning_count);
2946 array_init(&element);
2947 for (i = 0; i < error->warning_count; i++) {
2948 add_index_string(&element, error->warning_messages[i].position, error->warning_messages[i].message);
2949 }
2950 add_assoc_zval(z, "warnings", &element);
2951
2952 add_assoc_long(z, "error_count", error->error_count);
2953 array_init(&element);
2954 for (i = 0; i < error->error_count; i++) {
2955 add_index_string(&element, error->error_messages[i].position, error->error_messages[i].message);
2956 }
2957 add_assoc_zval(z, "errors", &element);
2958 } /* }}} */
2959
2960 /* {{{ Returns the warnings and errors found while parsing a date/time string. */
PHP_FUNCTION(date_get_last_errors)2961 PHP_FUNCTION(date_get_last_errors)
2962 {
2963 ZEND_PARSE_PARAMETERS_NONE();
2964
2965 if (DATEG(last_errors)) {
2966 array_init(return_value);
2967 zval_from_error_container(return_value, DATEG(last_errors));
2968 } else {
2969 RETURN_FALSE;
2970 }
2971 }
2972 /* }}} */
2973
php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS,timelib_time * parsed_time,timelib_error_container * error)2974 void php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAMETERS, timelib_time *parsed_time, timelib_error_container *error) /* {{{ */
2975 {
2976 zval element;
2977
2978 array_init(return_value);
2979 #define PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(name, elem) \
2980 if (parsed_time->elem == TIMELIB_UNSET) { \
2981 add_assoc_bool(return_value, #name, 0); \
2982 } else { \
2983 add_assoc_long(return_value, #name, parsed_time->elem); \
2984 }
2985 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(year, y);
2986 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(month, m);
2987 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(day, d);
2988 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(hour, h);
2989 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(minute, i);
2990 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(second, s);
2991
2992 if (parsed_time->us == TIMELIB_UNSET) {
2993 add_assoc_bool(return_value, "fraction", 0);
2994 } else {
2995 add_assoc_double(return_value, "fraction", (double)parsed_time->us / 1000000.0);
2996 }
2997
2998 zval_from_error_container(return_value, error);
2999
3000 timelib_error_container_dtor(error);
3001
3002 add_assoc_bool(return_value, "is_localtime", parsed_time->is_localtime);
3003
3004 if (parsed_time->is_localtime) {
3005 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone_type, zone_type);
3006 switch (parsed_time->zone_type) {
3007 case TIMELIB_ZONETYPE_OFFSET:
3008 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
3009 add_assoc_bool(return_value, "is_dst", parsed_time->dst);
3010 break;
3011 case TIMELIB_ZONETYPE_ID:
3012 if (parsed_time->tz_abbr) {
3013 add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
3014 }
3015 if (parsed_time->tz_info) {
3016 add_assoc_string(return_value, "tz_id", parsed_time->tz_info->name);
3017 }
3018 break;
3019 case TIMELIB_ZONETYPE_ABBR:
3020 PHP_DATE_PARSE_DATE_SET_TIME_ELEMENT(zone, z);
3021 add_assoc_bool(return_value, "is_dst", parsed_time->dst);
3022 add_assoc_string(return_value, "tz_abbr", parsed_time->tz_abbr);
3023 break;
3024 }
3025 }
3026 if (parsed_time->have_relative) {
3027 array_init(&element);
3028 add_assoc_long(&element, "year", parsed_time->relative.y);
3029 add_assoc_long(&element, "month", parsed_time->relative.m);
3030 add_assoc_long(&element, "day", parsed_time->relative.d);
3031 add_assoc_long(&element, "hour", parsed_time->relative.h);
3032 add_assoc_long(&element, "minute", parsed_time->relative.i);
3033 add_assoc_long(&element, "second", parsed_time->relative.s);
3034 if (parsed_time->relative.have_weekday_relative) {
3035 add_assoc_long(&element, "weekday", parsed_time->relative.weekday);
3036 }
3037 if (parsed_time->relative.have_special_relative && (parsed_time->relative.special.type == TIMELIB_SPECIAL_WEEKDAY)) {
3038 add_assoc_long(&element, "weekdays", parsed_time->relative.special.amount);
3039 }
3040 if (parsed_time->relative.first_last_day_of) {
3041 add_assoc_bool(&element, parsed_time->relative.first_last_day_of == TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH ? "first_day_of_month" : "last_day_of_month", 1);
3042 }
3043 add_assoc_zval(return_value, "relative", &element);
3044 }
3045 timelib_time_dtor(parsed_time);
3046 } /* }}} */
3047
3048 /* {{{ Returns associative array with detailed info about given date */
PHP_FUNCTION(date_parse)3049 PHP_FUNCTION(date_parse)
3050 {
3051 zend_string *date;
3052 timelib_error_container *error;
3053 timelib_time *parsed_time;
3054
3055 ZEND_PARSE_PARAMETERS_START(1, 1)
3056 Z_PARAM_STR(date)
3057 ZEND_PARSE_PARAMETERS_END();
3058
3059 parsed_time = timelib_strtotime(ZSTR_VAL(date), ZSTR_LEN(date), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3060 php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3061 }
3062 /* }}} */
3063
3064 /* {{{ Returns associative array with detailed info about given date */
PHP_FUNCTION(date_parse_from_format)3065 PHP_FUNCTION(date_parse_from_format)
3066 {
3067 zend_string *date, *format;
3068 timelib_error_container *error;
3069 timelib_time *parsed_time;
3070
3071 ZEND_PARSE_PARAMETERS_START(2, 2)
3072 Z_PARAM_STR(format)
3073 Z_PARAM_PATH_STR(date)
3074 ZEND_PARSE_PARAMETERS_END();
3075
3076 parsed_time = timelib_parse_from_format(ZSTR_VAL(format), ZSTR_VAL(date), ZSTR_LEN(date), &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3077 php_date_do_return_parsed_time(INTERNAL_FUNCTION_PARAM_PASSTHRU, parsed_time, error);
3078 }
3079 /* }}} */
3080
3081 /* {{{ Returns date formatted according to given format */
PHP_FUNCTION(date_format)3082 PHP_FUNCTION(date_format)
3083 {
3084 zval *object;
3085 php_date_obj *dateobj;
3086 char *format;
3087 size_t format_len;
3088
3089 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interface, &format, &format_len) == FAILURE) {
3090 RETURN_THROWS();
3091 }
3092 dateobj = Z_PHPDATE_P(object);
3093 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3094 RETURN_STR(date_format(format, format_len, dateobj->time, dateobj->time->is_localtime));
3095 }
3096 /* }}} */
3097
php_date_modify(zval * object,char * modify,size_t modify_len)3098 static bool php_date_modify(zval *object, char *modify, size_t modify_len) /* {{{ */
3099 {
3100 php_date_obj *dateobj;
3101 timelib_time *tmp_time;
3102 timelib_error_container *err = NULL;
3103
3104 dateobj = Z_PHPDATE_P(object);
3105
3106 if (!(dateobj->time)) {
3107 zend_throw_error(NULL, "The DateTime object has not been correctly initialized by its constructor");
3108 return 0;
3109 }
3110
3111 tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3112
3113 /* update last errors and warnings */
3114 update_errors_warnings(&err);
3115
3116 if (err && err->error_count) {
3117 /* spit out the first library error message, at least */
3118 php_error_docref(NULL, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify,
3119 err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message);
3120 timelib_time_dtor(tmp_time);
3121 return 0;
3122 }
3123
3124 memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(timelib_rel_time));
3125 dateobj->time->have_relative = tmp_time->have_relative;
3126 dateobj->time->sse_uptodate = 0;
3127
3128 if (tmp_time->y != TIMELIB_UNSET) {
3129 dateobj->time->y = tmp_time->y;
3130 }
3131 if (tmp_time->m != TIMELIB_UNSET) {
3132 dateobj->time->m = tmp_time->m;
3133 }
3134 if (tmp_time->d != TIMELIB_UNSET) {
3135 dateobj->time->d = tmp_time->d;
3136 }
3137
3138 if (tmp_time->h != TIMELIB_UNSET) {
3139 dateobj->time->h = tmp_time->h;
3140 if (tmp_time->i != TIMELIB_UNSET) {
3141 dateobj->time->i = tmp_time->i;
3142 if (tmp_time->s != TIMELIB_UNSET) {
3143 dateobj->time->s = tmp_time->s;
3144 } else {
3145 dateobj->time->s = 0;
3146 }
3147 } else {
3148 dateobj->time->i = 0;
3149 dateobj->time->s = 0;
3150 }
3151 }
3152
3153 if (tmp_time->us != TIMELIB_UNSET) {
3154 dateobj->time->us = tmp_time->us;
3155 }
3156
3157 /* Reset timezone to UTC if we detect a "@<ts>" modification */
3158 if (
3159 tmp_time->y == 1970 && tmp_time->m == 1 && tmp_time->d == 1 &&
3160 tmp_time->h == 0 && tmp_time->i == 0 && tmp_time->s == 0 && tmp_time->us == 0 &&
3161 tmp_time->have_zone && tmp_time->zone_type == TIMELIB_ZONETYPE_OFFSET &&
3162 tmp_time->z == 0 && tmp_time->dst == 0
3163 ) {
3164 timelib_set_timezone_from_offset(dateobj->time, 0);
3165 }
3166
3167 timelib_time_dtor(tmp_time);
3168
3169 timelib_update_ts(dateobj->time, NULL);
3170 timelib_update_from_sse(dateobj->time);
3171 dateobj->time->have_relative = 0;
3172 memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3173
3174 return 1;
3175 } /* }}} */
3176
3177 /* {{{ Alters the timestamp. */
PHP_FUNCTION(date_modify)3178 PHP_FUNCTION(date_modify)
3179 {
3180 zval *object;
3181 char *modify;
3182 size_t modify_len;
3183
3184 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) {
3185 RETURN_THROWS();
3186 }
3187
3188 if (!php_date_modify(object, modify, modify_len)) {
3189 RETURN_FALSE;
3190 }
3191
3192 RETURN_OBJ_COPY(Z_OBJ_P(object));
3193 }
3194 /* }}} */
3195
3196 /* {{{ */
PHP_METHOD(DateTimeImmutable,modify)3197 PHP_METHOD(DateTimeImmutable, modify)
3198 {
3199 zval *object, new_object;
3200 char *modify;
3201 size_t modify_len;
3202
3203 object = ZEND_THIS;
3204 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &modify, &modify_len) == FAILURE) {
3205 RETURN_THROWS();
3206 }
3207
3208 date_clone_immutable(object, &new_object);
3209 if (!php_date_modify(&new_object, modify, modify_len)) {
3210 zval_ptr_dtor(&new_object);
3211 RETURN_FALSE;
3212 }
3213
3214 RETURN_OBJ(Z_OBJ(new_object));
3215 }
3216 /* }}} */
3217
php_date_add(zval * object,zval * interval,zval * return_value)3218 static void php_date_add(zval *object, zval *interval, zval *return_value) /* {{{ */
3219 {
3220 php_date_obj *dateobj;
3221 php_interval_obj *intobj;
3222 timelib_time *new_time;
3223
3224 dateobj = Z_PHPDATE_P(object);
3225 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3226 intobj = Z_PHPINTERVAL_P(interval);
3227 DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3228
3229 if (intobj->civil_or_wall == PHP_DATE_WALL) {
3230 new_time = timelib_add_wall(dateobj->time, intobj->diff);
3231 } else {
3232 new_time = timelib_add(dateobj->time, intobj->diff);
3233 }
3234 timelib_time_dtor(dateobj->time);
3235 dateobj->time = new_time;
3236 } /* }}} */
3237
3238 /* {{{ Adds an interval to the current date in object. */
PHP_FUNCTION(date_add)3239 PHP_FUNCTION(date_add)
3240 {
3241 zval *object, *interval;
3242
3243 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3244 RETURN_THROWS();
3245 }
3246
3247 php_date_add(object, interval, return_value);
3248
3249 RETURN_OBJ_COPY(Z_OBJ_P(object));
3250 }
3251 /* }}} */
3252
3253 /* {{{ */
PHP_METHOD(DateTimeImmutable,add)3254 PHP_METHOD(DateTimeImmutable, add)
3255 {
3256 zval *object, *interval, new_object;
3257
3258 object = ZEND_THIS;
3259 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &interval, date_ce_interval) == FAILURE) {
3260 RETURN_THROWS();
3261 }
3262
3263 date_clone_immutable(object, &new_object);
3264 php_date_add(&new_object, interval, return_value);
3265
3266 RETURN_OBJ(Z_OBJ(new_object));
3267 }
3268 /* }}} */
3269
php_date_sub(zval * object,zval * interval,zval * return_value)3270 static void php_date_sub(zval *object, zval *interval, zval *return_value) /* {{{ */
3271 {
3272 php_date_obj *dateobj;
3273 php_interval_obj *intobj;
3274 timelib_time *new_time;
3275
3276 dateobj = Z_PHPDATE_P(object);
3277 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3278 intobj = Z_PHPINTERVAL_P(interval);
3279 DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
3280
3281 if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) {
3282 php_error_docref(NULL, E_WARNING, "Only non-special relative time specifications are supported for subtraction");
3283 return;
3284 }
3285
3286 if (intobj->civil_or_wall == PHP_DATE_WALL) {
3287 new_time = timelib_sub_wall(dateobj->time, intobj->diff);
3288 } else {
3289 new_time = timelib_sub(dateobj->time, intobj->diff);
3290 }
3291 timelib_time_dtor(dateobj->time);
3292 dateobj->time = new_time;
3293 } /* }}} */
3294
3295 /* {{{ Subtracts an interval to the current date in object. */
PHP_FUNCTION(date_sub)3296 PHP_FUNCTION(date_sub)
3297 {
3298 zval *object, *interval;
3299
3300 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) {
3301 RETURN_THROWS();
3302 }
3303
3304 php_date_sub(object, interval, return_value);
3305
3306 RETURN_OBJ_COPY(Z_OBJ_P(object));
3307 }
3308 /* }}} */
3309
3310 /* {{{ */
PHP_METHOD(DateTimeImmutable,sub)3311 PHP_METHOD(DateTimeImmutable, sub)
3312 {
3313 zval *object, *interval, new_object;
3314
3315 object = ZEND_THIS;
3316 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &interval, date_ce_interval) == FAILURE) {
3317 RETURN_THROWS();
3318 }
3319
3320 date_clone_immutable(object, &new_object);
3321 php_date_sub(&new_object, interval, return_value);
3322
3323 RETURN_OBJ(Z_OBJ(new_object));
3324 }
3325 /* }}} */
3326
set_timezone_from_timelib_time(php_timezone_obj * tzobj,timelib_time * t)3327 static void set_timezone_from_timelib_time(php_timezone_obj *tzobj, timelib_time *t)
3328 {
3329 /* Free abbreviation if already set */
3330 if (tzobj->initialized && tzobj->type == TIMELIB_ZONETYPE_ABBR) {
3331 timelib_free(tzobj->tzi.z.abbr);
3332 }
3333
3334 /* Set new values */
3335 tzobj->initialized = 1;
3336 tzobj->type = t->zone_type;
3337
3338 switch (t->zone_type) {
3339 case TIMELIB_ZONETYPE_ID:
3340 tzobj->tzi.tz = t->tz_info;
3341 break;
3342 case TIMELIB_ZONETYPE_OFFSET:
3343 tzobj->tzi.utc_offset = t->z;
3344 break;
3345 case TIMELIB_ZONETYPE_ABBR:
3346 tzobj->tzi.z.utc_offset = t->z;
3347 tzobj->tzi.z.dst = t->dst;
3348 tzobj->tzi.z.abbr = timelib_strdup(t->tz_abbr);
3349 break;
3350 }
3351 }
3352
3353
3354 /* {{{ Return new DateTimeZone object relative to give DateTime */
PHP_FUNCTION(date_timezone_get)3355 PHP_FUNCTION(date_timezone_get)
3356 {
3357 zval *object;
3358 php_date_obj *dateobj;
3359
3360 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3361 RETURN_THROWS();
3362 }
3363 dateobj = Z_PHPDATE_P(object);
3364 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3365 if (dateobj->time->is_localtime) {
3366 php_timezone_obj *tzobj;
3367 php_date_instantiate(date_ce_timezone, return_value);
3368 tzobj = Z_PHPTIMEZONE_P(return_value);
3369 set_timezone_from_timelib_time(tzobj, dateobj->time);
3370 } else {
3371 RETURN_FALSE;
3372 }
3373 }
3374 /* }}} */
3375
php_date_timezone_set(zval * object,zval * timezone_object,zval * return_value)3376 static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value) /* {{{ */
3377 {
3378 php_date_obj *dateobj;
3379 php_timezone_obj *tzobj;
3380
3381 dateobj = Z_PHPDATE_P(object);
3382 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3383 tzobj = Z_PHPTIMEZONE_P(timezone_object);
3384
3385 switch (tzobj->type) {
3386 case TIMELIB_ZONETYPE_OFFSET:
3387 timelib_set_timezone_from_offset(dateobj->time, tzobj->tzi.utc_offset);
3388 break;
3389 case TIMELIB_ZONETYPE_ABBR:
3390 timelib_set_timezone_from_abbr(dateobj->time, tzobj->tzi.z);
3391 break;
3392 case TIMELIB_ZONETYPE_ID:
3393 timelib_set_timezone(dateobj->time, tzobj->tzi.tz);
3394 break;
3395 }
3396 timelib_unixtime2local(dateobj->time, dateobj->time->sse);
3397 } /* }}} */
3398
3399 /* {{{ Sets the timezone for the DateTime object. */
PHP_FUNCTION(date_timezone_set)3400 PHP_FUNCTION(date_timezone_set)
3401 {
3402 zval *object;
3403 zval *timezone_object;
3404
3405 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) {
3406 RETURN_THROWS();
3407 }
3408
3409 php_date_timezone_set(object, timezone_object, return_value);
3410
3411 RETURN_OBJ_COPY(Z_OBJ_P(object));
3412 }
3413 /* }}} */
3414
3415 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTimezone)3416 PHP_METHOD(DateTimeImmutable, setTimezone)
3417 {
3418 zval *object, new_object;
3419 zval *timezone_object;
3420
3421 object = ZEND_THIS;
3422 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &timezone_object, date_ce_timezone) == FAILURE) {
3423 RETURN_THROWS();
3424 }
3425
3426 date_clone_immutable(object, &new_object);
3427 php_date_timezone_set(&new_object, timezone_object, return_value);
3428
3429 RETURN_OBJ(Z_OBJ(new_object));
3430 }
3431 /* }}} */
3432
3433 /* {{{ Returns the DST offset. */
PHP_FUNCTION(date_offset_get)3434 PHP_FUNCTION(date_offset_get)
3435 {
3436 zval *object;
3437 php_date_obj *dateobj;
3438 timelib_time_offset *offset;
3439
3440 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3441 RETURN_THROWS();
3442 }
3443 dateobj = Z_PHPDATE_P(object);
3444 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3445 if (dateobj->time->is_localtime) {
3446 switch (dateobj->time->zone_type) {
3447 case TIMELIB_ZONETYPE_ID:
3448 offset = timelib_get_time_zone_info(dateobj->time->sse, dateobj->time->tz_info);
3449 RETVAL_LONG(offset->offset);
3450 timelib_time_offset_dtor(offset);
3451 break;
3452 case TIMELIB_ZONETYPE_OFFSET:
3453 RETVAL_LONG(dateobj->time->z);
3454 break;
3455 case TIMELIB_ZONETYPE_ABBR:
3456 RETVAL_LONG((dateobj->time->z + (3600 * dateobj->time->dst)));
3457 break;
3458 }
3459 return;
3460 } else {
3461 RETURN_LONG(0);
3462 }
3463 }
3464 /* }}} */
3465
php_date_time_set(zval * object,zend_long h,zend_long i,zend_long s,zend_long ms,zval * return_value)3466 static void php_date_time_set(zval *object, zend_long h, zend_long i, zend_long s, zend_long ms, zval *return_value) /* {{{ */
3467 {
3468 php_date_obj *dateobj;
3469
3470 dateobj = Z_PHPDATE_P(object);
3471 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3472 dateobj->time->h = h;
3473 dateobj->time->i = i;
3474 dateobj->time->s = s;
3475 dateobj->time->us = ms;
3476 timelib_update_ts(dateobj->time, NULL);
3477 timelib_update_from_sse(dateobj->time);
3478 } /* }}} */
3479
3480 /* {{{ Sets the time. */
PHP_FUNCTION(date_time_set)3481 PHP_FUNCTION(date_time_set)
3482 {
3483 zval *object;
3484 zend_long h, i, s = 0, ms = 0;
3485
3486 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|ll", &object, date_ce_date, &h, &i, &s, &ms) == FAILURE) {
3487 RETURN_THROWS();
3488 }
3489
3490 php_date_time_set(object, h, i, s, ms, return_value);
3491
3492 RETURN_OBJ_COPY(Z_OBJ_P(object));
3493 }
3494 /* }}} */
3495
3496 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTime)3497 PHP_METHOD(DateTimeImmutable, setTime)
3498 {
3499 zval *object, new_object;
3500 zend_long h, i, s = 0, ms = 0;
3501
3502 object = ZEND_THIS;
3503 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|ll", &h, &i, &s, &ms) == FAILURE) {
3504 RETURN_THROWS();
3505 }
3506
3507 date_clone_immutable(object, &new_object);
3508 php_date_time_set(&new_object, h, i, s, ms, return_value);
3509
3510 RETURN_OBJ(Z_OBJ(new_object));
3511 }
3512 /* }}} */
3513
php_date_date_set(zval * object,zend_long y,zend_long m,zend_long d,zval * return_value)3514 static void php_date_date_set(zval *object, zend_long y, zend_long m, zend_long d, zval *return_value) /* {{{ */
3515 {
3516 php_date_obj *dateobj;
3517
3518 dateobj = Z_PHPDATE_P(object);
3519 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3520 dateobj->time->y = y;
3521 dateobj->time->m = m;
3522 dateobj->time->d = d;
3523 timelib_update_ts(dateobj->time, NULL);
3524 } /* }}} */
3525
3526 /* {{{ Sets the date. */
PHP_FUNCTION(date_date_set)3527 PHP_FUNCTION(date_date_set)
3528 {
3529 zval *object;
3530 zend_long y, m, d;
3531
3532 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) {
3533 RETURN_THROWS();
3534 }
3535
3536 php_date_date_set(object, y, m, d, return_value);
3537
3538 RETURN_OBJ_COPY(Z_OBJ_P(object));
3539 }
3540 /* }}} */
3541
3542 /* {{{ */
PHP_METHOD(DateTimeImmutable,setDate)3543 PHP_METHOD(DateTimeImmutable, setDate)
3544 {
3545 zval *object, new_object;
3546 zend_long y, m, d;
3547
3548 object = ZEND_THIS;
3549 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &y, &m, &d) == FAILURE) {
3550 RETURN_THROWS();
3551 }
3552
3553 date_clone_immutable(object, &new_object);
3554 php_date_date_set(&new_object, y, m, d, return_value);
3555
3556 RETURN_OBJ(Z_OBJ(new_object));
3557 }
3558 /* }}} */
3559
php_date_isodate_set(zval * object,zend_long y,zend_long w,zend_long d,zval * return_value)3560 static void php_date_isodate_set(zval *object, zend_long y, zend_long w, zend_long d, zval *return_value) /* {{{ */
3561 {
3562 php_date_obj *dateobj;
3563
3564 dateobj = Z_PHPDATE_P(object);
3565 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3566 dateobj->time->y = y;
3567 dateobj->time->m = 1;
3568 dateobj->time->d = 1;
3569 memset(&dateobj->time->relative, 0, sizeof(dateobj->time->relative));
3570 dateobj->time->relative.d = timelib_daynr_from_weeknr(y, w, d);
3571 dateobj->time->have_relative = 1;
3572
3573 timelib_update_ts(dateobj->time, NULL);
3574 } /* }}} */
3575
3576 /* {{{ Sets the ISO date. */
PHP_FUNCTION(date_isodate_set)3577 PHP_FUNCTION(date_isodate_set)
3578 {
3579 zval *object;
3580 zend_long y, w, d = 1;
3581
3582 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) {
3583 RETURN_THROWS();
3584 }
3585
3586 php_date_isodate_set(object, y, w, d, return_value);
3587
3588 RETURN_OBJ_COPY(Z_OBJ_P(object));
3589 }
3590 /* }}} */
3591
3592 /* {{{ */
PHP_METHOD(DateTimeImmutable,setISODate)3593 PHP_METHOD(DateTimeImmutable, setISODate)
3594 {
3595 zval *object, new_object;
3596 zend_long y, w, d = 1;
3597
3598 object = ZEND_THIS;
3599 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|l", &y, &w, &d) == FAILURE) {
3600 RETURN_THROWS();
3601 }
3602
3603 date_clone_immutable(object, &new_object);
3604 php_date_isodate_set(&new_object, y, w, d, return_value);
3605
3606 RETURN_OBJ(Z_OBJ(new_object));
3607 }
3608 /* }}} */
3609
php_date_timestamp_set(zval * object,zend_long timestamp,zval * return_value)3610 static void php_date_timestamp_set(zval *object, zend_long timestamp, zval *return_value) /* {{{ */
3611 {
3612 php_date_obj *dateobj;
3613
3614 dateobj = Z_PHPDATE_P(object);
3615 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3616 timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp);
3617 timelib_update_ts(dateobj->time, NULL);
3618 php_date_set_time_fraction(dateobj->time, 0);
3619 } /* }}} */
3620
3621 /* {{{ Sets the date and time based on an Unix timestamp. */
PHP_FUNCTION(date_timestamp_set)3622 PHP_FUNCTION(date_timestamp_set)
3623 {
3624 zval *object;
3625 zend_long timestamp;
3626
3627 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Ol", &object, date_ce_date, ×tamp) == FAILURE) {
3628 RETURN_THROWS();
3629 }
3630
3631 php_date_timestamp_set(object, timestamp, return_value);
3632
3633 RETURN_OBJ_COPY(Z_OBJ_P(object));
3634 }
3635 /* }}} */
3636
3637 /* {{{ */
PHP_METHOD(DateTimeImmutable,setTimestamp)3638 PHP_METHOD(DateTimeImmutable, setTimestamp)
3639 {
3640 zval *object, new_object;
3641 zend_long timestamp;
3642
3643 object = ZEND_THIS;
3644 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", ×tamp) == FAILURE) {
3645 RETURN_THROWS();
3646 }
3647
3648 date_clone_immutable(object, &new_object);
3649 php_date_timestamp_set(&new_object, timestamp, return_value);
3650
3651 RETURN_OBJ(Z_OBJ(new_object));
3652 }
3653 /* }}} */
3654
3655 /* {{{ Gets the Unix timestamp. */
PHP_FUNCTION(date_timestamp_get)3656 PHP_FUNCTION(date_timestamp_get)
3657 {
3658 zval *object;
3659 php_date_obj *dateobj;
3660 zend_long timestamp;
3661 int epoch_does_not_fit_in_zend_long;
3662
3663 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_interface) == FAILURE) {
3664 RETURN_THROWS();
3665 }
3666 dateobj = Z_PHPDATE_P(object);
3667 DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
3668
3669 if (!dateobj->time->sse_uptodate) {
3670 timelib_update_ts(dateobj->time, NULL);
3671 }
3672
3673 timestamp = timelib_date_to_int(dateobj->time, &epoch_does_not_fit_in_zend_long);
3674
3675 if (epoch_does_not_fit_in_zend_long) {
3676 zend_value_error("Epoch doesn't fit in a PHP integer");
3677 RETURN_THROWS();
3678 }
3679
3680 RETURN_LONG(timestamp);
3681 }
3682 /* }}} */
3683
3684 /* {{{ Returns the difference between two DateTime objects. */
PHP_FUNCTION(date_diff)3685 PHP_FUNCTION(date_diff)
3686 {
3687 zval *object1, *object2;
3688 php_date_obj *dateobj1, *dateobj2;
3689 php_interval_obj *interval;
3690 bool absolute = 0;
3691
3692 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO|b", &object1, date_ce_interface, &object2, date_ce_interface, &absolute) == FAILURE) {
3693 RETURN_THROWS();
3694 }
3695 dateobj1 = Z_PHPDATE_P(object1);
3696 dateobj2 = Z_PHPDATE_P(object2);
3697 DATE_CHECK_INITIALIZED(dateobj1->time, DateTimeInterface);
3698 DATE_CHECK_INITIALIZED(dateobj2->time, DateTimeInterface);
3699
3700 php_date_instantiate(date_ce_interval, return_value);
3701 interval = Z_PHPINTERVAL_P(return_value);
3702 interval->diff = timelib_diff(dateobj1->time, dateobj2->time);
3703 if (absolute) {
3704 interval->diff->invert = 0;
3705 }
3706 interval->initialized = 1;
3707 interval->civil_or_wall = PHP_DATE_CIVIL;
3708 }
3709 /* }}} */
3710
timezone_initialize(php_timezone_obj * tzobj,const char * tz,size_t tz_len)3711 static bool timezone_initialize(php_timezone_obj *tzobj, const char *tz, size_t tz_len) /* {{{ */
3712 {
3713 timelib_time *dummy_t = ecalloc(1, sizeof(timelib_time));
3714 int dst, not_found;
3715 const char *orig_tz = tz;
3716
3717 if (strlen(tz) != tz_len) {
3718 php_error_docref(NULL, E_WARNING, "Timezone must not contain null bytes");
3719 efree(dummy_t);
3720 return false;
3721 }
3722
3723 dummy_t->z = timelib_parse_zone(&tz, &dst, dummy_t, ¬_found, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
3724 if ((dummy_t->z >= (100 * 60 * 60)) || (dummy_t->z <= (-100 * 60 * 60))) {
3725 php_error_docref(NULL, E_WARNING, "Timezone offset is out of range (%s)", orig_tz);
3726 timelib_free(dummy_t->tz_abbr);
3727 efree(dummy_t);
3728 return FAILURE;
3729 }
3730 dummy_t->dst = dst;
3731 if (!not_found && (*tz != '\0')) {
3732 php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3733 timelib_free(dummy_t->tz_abbr);
3734 efree(dummy_t);
3735 return false;
3736 }
3737 if (not_found) {
3738 php_error_docref(NULL, E_WARNING, "Unknown or bad timezone (%s)", orig_tz);
3739 efree(dummy_t);
3740 return false;
3741 } else {
3742 set_timezone_from_timelib_time(tzobj, dummy_t);
3743 timelib_free(dummy_t->tz_abbr);
3744 efree(dummy_t);
3745 return true;
3746 }
3747 } /* }}} */
3748
3749 /* {{{ Returns new DateTimeZone object */
PHP_FUNCTION(timezone_open)3750 PHP_FUNCTION(timezone_open)
3751 {
3752 zend_string *tz;
3753 php_timezone_obj *tzobj;
3754
3755 ZEND_PARSE_PARAMETERS_START(1, 1)
3756 Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
3757 ZEND_PARSE_PARAMETERS_END();
3758
3759 tzobj = Z_PHPTIMEZONE_P(php_date_instantiate(date_ce_timezone, return_value));
3760 if (!timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz))) {
3761 zval_ptr_dtor(return_value);
3762 RETURN_FALSE;
3763 }
3764 }
3765 /* }}} */
3766
3767 /* {{{ Creates new DateTimeZone object. */
PHP_METHOD(DateTimeZone,__construct)3768 PHP_METHOD(DateTimeZone, __construct)
3769 {
3770 zend_string *tz;
3771 php_timezone_obj *tzobj;
3772 zend_error_handling error_handling;
3773
3774 ZEND_PARSE_PARAMETERS_START(1, 1)
3775 Z_PARAM_PATH_STR(tz) /* To prevent null bytes */
3776 ZEND_PARSE_PARAMETERS_END();
3777
3778 zend_replace_error_handling(EH_THROW, NULL, &error_handling);
3779 tzobj = Z_PHPTIMEZONE_P(ZEND_THIS);
3780 timezone_initialize(tzobj, ZSTR_VAL(tz), ZSTR_LEN(tz));
3781 zend_restore_error_handling(&error_handling);
3782 }
3783 /* }}} */
3784
php_date_timezone_initialize_from_hash(zval ** return_value,php_timezone_obj ** tzobj,HashTable * myht)3785 static bool php_date_timezone_initialize_from_hash(zval **return_value, php_timezone_obj **tzobj, HashTable *myht) /* {{{ */
3786 {
3787 zval *z_timezone_type;
3788
3789 if ((z_timezone_type = zend_hash_str_find(myht, "timezone_type", sizeof("timezone_type") - 1)) == NULL) {
3790 return false;
3791 }
3792
3793 zval *z_timezone;
3794
3795 if ((z_timezone = zend_hash_str_find(myht, "timezone", sizeof("timezone") - 1)) == NULL) {
3796 return false;
3797 }
3798
3799 if (Z_TYPE_P(z_timezone_type) != IS_LONG) {
3800 return false;
3801 }
3802 if (Z_TYPE_P(z_timezone) != IS_STRING) {
3803 return false;
3804 }
3805 return timezone_initialize(*tzobj, Z_STRVAL_P(z_timezone), Z_STRLEN_P(z_timezone));
3806 } /* }}} */
3807
3808 /* {{{ */
PHP_METHOD(DateTimeZone,__set_state)3809 PHP_METHOD(DateTimeZone, __set_state)
3810 {
3811 php_timezone_obj *tzobj;
3812 zval *array;
3813 HashTable *myht;
3814
3815 ZEND_PARSE_PARAMETERS_START(1, 1)
3816 Z_PARAM_ARRAY(array)
3817 ZEND_PARSE_PARAMETERS_END();
3818
3819 myht = Z_ARRVAL_P(array);
3820
3821 php_date_instantiate(date_ce_timezone, return_value);
3822 tzobj = Z_PHPTIMEZONE_P(return_value);
3823 if (!php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht)) {
3824 zend_throw_error(NULL, "Timezone initialization failed");
3825 RETURN_THROWS();
3826 }
3827 }
3828 /* }}} */
3829
3830 /* {{{ */
PHP_METHOD(DateTimeZone,__wakeup)3831 PHP_METHOD(DateTimeZone, __wakeup)
3832 {
3833 zval *object = ZEND_THIS;
3834 php_timezone_obj *tzobj;
3835 HashTable *myht;
3836
3837 ZEND_PARSE_PARAMETERS_NONE();
3838
3839 tzobj = Z_PHPTIMEZONE_P(object);
3840
3841 myht = Z_OBJPROP_P(object);
3842
3843 if (!php_date_timezone_initialize_from_hash(&return_value, &tzobj, myht)) {
3844 zend_throw_error(NULL, "Timezone initialization failed");
3845 RETURN_THROWS();
3846 }
3847 }
3848 /* }}} */
3849
3850 /* {{{ */
PHP_METHOD(DateTimeZone,__serialize)3851 PHP_METHOD(DateTimeZone, __serialize)
3852 {
3853 zval *object = ZEND_THIS;
3854 php_timezone_obj *tzobj;
3855 HashTable *myht;
3856
3857 ZEND_PARSE_PARAMETERS_NONE();
3858
3859 tzobj = Z_PHPTIMEZONE_P(object);
3860 DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3861
3862 array_init(return_value);
3863 myht = Z_ARRVAL_P(return_value);
3864 date_timezone_object_to_hash(tzobj, myht);
3865
3866 add_common_properties(myht, &tzobj->std);
3867 }
3868 /* }}} */
3869
date_timezone_is_internal_property(zend_string * name)3870 static bool date_timezone_is_internal_property(zend_string *name)
3871 {
3872 if (
3873 zend_string_equals_literal(name, "timezone_type") ||
3874 zend_string_equals_literal(name, "timezone")
3875 ) {
3876 return 1;
3877 }
3878 return 0;
3879 }
3880
restore_custom_datetimezone_properties(zval * object,HashTable * myht)3881 static void restore_custom_datetimezone_properties(zval *object, HashTable *myht)
3882 {
3883 zend_string *prop_name;
3884 zval *prop_val;
3885
3886 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
3887 if (!prop_name || (Z_TYPE_P(prop_val) == IS_REFERENCE) || date_timezone_is_internal_property(prop_name)) {
3888 continue;
3889 }
3890 update_property(Z_OBJ_P(object), prop_name, prop_val);
3891 } ZEND_HASH_FOREACH_END();
3892 }
3893
3894 /* {{{ */
PHP_METHOD(DateTimeZone,__unserialize)3895 PHP_METHOD(DateTimeZone, __unserialize)
3896 {
3897 zval *object = ZEND_THIS;
3898 php_timezone_obj *tzobj;
3899 zval *array;
3900 HashTable *myht;
3901
3902 ZEND_PARSE_PARAMETERS_START(1, 1)
3903 Z_PARAM_ARRAY(array)
3904 ZEND_PARSE_PARAMETERS_END();
3905
3906 tzobj = Z_PHPTIMEZONE_P(object);
3907 myht = Z_ARRVAL_P(array);
3908
3909 if (!php_date_timezone_initialize_from_hash(&object, &tzobj, myht)) {
3910 zend_throw_error(NULL, "Invalid serialization data for DateTimeZone object");
3911 RETURN_THROWS();
3912 }
3913
3914 restore_custom_datetimezone_properties(object, myht);
3915 }
3916 /* }}} */
3917
3918 /* {{{ Returns the name of the timezone. */
PHP_FUNCTION(timezone_name_get)3919 PHP_FUNCTION(timezone_name_get)
3920 {
3921 zval *object;
3922 php_timezone_obj *tzobj;
3923
3924 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
3925 RETURN_THROWS();
3926 }
3927 tzobj = Z_PHPTIMEZONE_P(object);
3928 DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3929 php_timezone_to_string(tzobj, return_value);
3930 }
3931 /* }}} */
3932
3933 /* {{{ Returns the timezone name from abbreviation */
PHP_FUNCTION(timezone_name_from_abbr)3934 PHP_FUNCTION(timezone_name_from_abbr)
3935 {
3936 zend_string *abbr;
3937 const char *tzid;
3938 zend_long gmtoffset = -1;
3939 zend_long isdst = -1;
3940
3941 ZEND_PARSE_PARAMETERS_START(1, 3)
3942 Z_PARAM_STR(abbr)
3943 Z_PARAM_OPTIONAL
3944 Z_PARAM_LONG(gmtoffset)
3945 Z_PARAM_LONG(isdst)
3946 ZEND_PARSE_PARAMETERS_END();
3947
3948 tzid = timelib_timezone_id_from_abbr(ZSTR_VAL(abbr), gmtoffset, isdst);
3949
3950 if (tzid) {
3951 RETURN_STRING(tzid);
3952 } else {
3953 RETURN_FALSE;
3954 }
3955 }
3956 /* }}} */
3957
3958 /* {{{ Returns the timezone offset. */
PHP_FUNCTION(timezone_offset_get)3959 PHP_FUNCTION(timezone_offset_get)
3960 {
3961 zval *object, *dateobject;
3962 php_timezone_obj *tzobj;
3963 php_date_obj *dateobj;
3964 timelib_time_offset *offset;
3965
3966 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OO", &object, date_ce_timezone, &dateobject, date_ce_interface) == FAILURE) {
3967 RETURN_THROWS();
3968 }
3969 tzobj = Z_PHPTIMEZONE_P(object);
3970 DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
3971 dateobj = Z_PHPDATE_P(dateobject);
3972 DATE_CHECK_INITIALIZED(dateobj->time, DateTimeInterface);
3973
3974 switch (tzobj->type) {
3975 case TIMELIB_ZONETYPE_ID:
3976 offset = timelib_get_time_zone_info(dateobj->time->sse, tzobj->tzi.tz);
3977 RETVAL_LONG(offset->offset);
3978 timelib_time_offset_dtor(offset);
3979 break;
3980 case TIMELIB_ZONETYPE_OFFSET:
3981 RETURN_LONG(tzobj->tzi.utc_offset);
3982 break;
3983 case TIMELIB_ZONETYPE_ABBR:
3984 RETURN_LONG(tzobj->tzi.z.utc_offset + (tzobj->tzi.z.dst * 3600));
3985 break;
3986 }
3987 }
3988 /* }}} */
3989
3990 /* {{{ Returns numerically indexed array containing associative array for all transitions in the specified range for the timezone. */
PHP_FUNCTION(timezone_transitions_get)3991 PHP_FUNCTION(timezone_transitions_get)
3992 {
3993 zval *object, element;
3994 php_timezone_obj *tzobj;
3995 int begin = 0;
3996 bool found;
3997 zend_long timestamp_begin = ZEND_LONG_MIN, timestamp_end = INT32_MAX;
3998
3999 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ll", &object, date_ce_timezone, ×tamp_begin, ×tamp_end) == FAILURE) {
4000 RETURN_THROWS();
4001 }
4002 tzobj = Z_PHPTIMEZONE_P(object);
4003 DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
4004 if (tzobj->type != TIMELIB_ZONETYPE_ID) {
4005 RETURN_FALSE;
4006 }
4007
4008 #define add_nominal() \
4009 array_init(&element); \
4010 add_assoc_long(&element, "ts", timestamp_begin); \
4011 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, timestamp_begin, 0)); \
4012 add_assoc_long(&element, "offset", tzobj->tzi.tz->type[0].offset); \
4013 add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[0].isdst); \
4014 add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[0].abbr_idx]); \
4015 add_next_index_zval(return_value, &element);
4016
4017 #define add(i,ts) \
4018 array_init(&element); \
4019 add_assoc_long(&element, "ts", ts); \
4020 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, ts, 0)); \
4021 add_assoc_long(&element, "offset", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].offset); \
4022 add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].isdst); \
4023 add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[tzobj->tzi.tz->trans_idx[i]].abbr_idx]); \
4024 add_next_index_zval(return_value, &element);
4025
4026 #define add_by_index(i,ts) \
4027 array_init(&element); \
4028 add_assoc_long(&element, "ts", ts); \
4029 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, ts, 0)); \
4030 add_assoc_long(&element, "offset", tzobj->tzi.tz->type[i].offset); \
4031 add_assoc_bool(&element, "isdst", tzobj->tzi.tz->type[i].isdst); \
4032 add_assoc_string(&element, "abbr", &tzobj->tzi.tz->timezone_abbr[tzobj->tzi.tz->type[i].abbr_idx]); \
4033 add_next_index_zval(return_value, &element);
4034
4035 #define add_from_tto(to,ts) \
4036 array_init(&element); \
4037 add_assoc_long(&element, "ts", ts); \
4038 add_assoc_str(&element, "time", php_format_date(DATE_FORMAT_ISO8601_LARGE_YEAR, 13, ts, 0)); \
4039 add_assoc_long(&element, "offset", (to)->offset); \
4040 add_assoc_bool(&element, "isdst", (to)->is_dst); \
4041 add_assoc_string(&element, "abbr", (to)->abbr); \
4042 add_next_index_zval(return_value, &element);
4043
4044 #define add_last() add(tzobj->tzi.tz->bit64.timecnt - 1, timestamp_begin)
4045
4046 array_init(return_value);
4047
4048 if (timestamp_begin == ZEND_LONG_MIN) {
4049 add_nominal();
4050 begin = 0;
4051 found = 1;
4052 } else {
4053 begin = 0;
4054 found = 0;
4055 if (tzobj->tzi.tz->bit64.timecnt > 0) {
4056 do {
4057 if (tzobj->tzi.tz->trans[begin] > timestamp_begin) {
4058 if (begin > 0) {
4059 add(begin - 1, timestamp_begin);
4060 } else {
4061 add_nominal();
4062 }
4063 found = 1;
4064 break;
4065 }
4066 begin++;
4067 } while (begin < tzobj->tzi.tz->bit64.timecnt);
4068 }
4069 }
4070
4071 if (!found) {
4072 if (tzobj->tzi.tz->bit64.timecnt > 0) {
4073 if (tzobj->tzi.tz->posix_info && tzobj->tzi.tz->posix_info->dst_end) {
4074 timelib_time_offset *tto = timelib_get_time_zone_info(timestamp_begin, tzobj->tzi.tz);
4075 add_from_tto(tto, timestamp_begin);
4076 timelib_time_offset_dtor(tto);
4077 } else {
4078 add_last();
4079 }
4080 } else {
4081 add_nominal();
4082 }
4083 } else {
4084 unsigned int i;
4085 for (i = begin; i < tzobj->tzi.tz->bit64.timecnt; ++i) {
4086 if (tzobj->tzi.tz->trans[i] < timestamp_end) {
4087 add(i, tzobj->tzi.tz->trans[i]);
4088 } else {
4089 return;
4090 }
4091 }
4092 }
4093 if (tzobj->tzi.tz->posix_info && tzobj->tzi.tz->posix_info->dst_end) {
4094 int i, j;
4095 timelib_sll start_y, end_y, dummy_m, dummy_d;
4096 timelib_sll last_transition_ts = tzobj->tzi.tz->trans[tzobj->tzi.tz->bit64.timecnt - 1];
4097
4098 /* Find out year for last transition */
4099 timelib_unixtime2date(last_transition_ts, &start_y, &dummy_m, &dummy_d);
4100
4101 /* Find out year for final boundary timestamp */
4102 timelib_unixtime2date(timestamp_end, &end_y, &dummy_m, &dummy_d);
4103
4104 for (i = start_y; i <= end_y; i++) {
4105 timelib_posix_transitions transitions = { 0 };
4106
4107 timelib_get_transitions_for_year(tzobj->tzi.tz, i, &transitions);
4108
4109 for (j = 0; j < transitions.count; j++) {
4110 if (transitions.times[j] <= last_transition_ts) {
4111 continue;
4112 }
4113 if (transitions.times[j] < timestamp_begin) {
4114 continue;
4115 }
4116 if (transitions.times[j] > timestamp_end) {
4117 return;
4118 }
4119 add_by_index(transitions.types[j], transitions.times[j]);
4120 }
4121 }
4122 }
4123 }
4124 /* }}} */
4125
4126 /* {{{ Returns location information for a timezone, including country code, latitude/longitude and comments */
PHP_FUNCTION(timezone_location_get)4127 PHP_FUNCTION(timezone_location_get)
4128 {
4129 zval *object;
4130 php_timezone_obj *tzobj;
4131
4132 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &object, date_ce_timezone) == FAILURE) {
4133 RETURN_THROWS();
4134 }
4135 tzobj = Z_PHPTIMEZONE_P(object);
4136 DATE_CHECK_INITIALIZED(tzobj->initialized, DateTimeZone);
4137 if (tzobj->type != TIMELIB_ZONETYPE_ID) {
4138 RETURN_FALSE;
4139 }
4140
4141 array_init(return_value);
4142 add_assoc_string(return_value, "country_code", tzobj->tzi.tz->location.country_code);
4143 add_assoc_double(return_value, "latitude", tzobj->tzi.tz->location.latitude);
4144 add_assoc_double(return_value, "longitude", tzobj->tzi.tz->location.longitude);
4145 add_assoc_string(return_value, "comments", tzobj->tzi.tz->location.comments);
4146 }
4147 /* }}} */
4148
date_interval_initialize(timelib_rel_time ** rt,char * format,size_t format_length)4149 static bool date_interval_initialize(timelib_rel_time **rt, /*const*/ char *format, size_t format_length) /* {{{ */
4150 {
4151 timelib_time *b = NULL, *e = NULL;
4152 timelib_rel_time *p = NULL;
4153 int r = 0;
4154 bool retval = false;
4155 timelib_error_container *errors;
4156
4157 timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4158
4159 if (errors->error_count > 0) {
4160 zend_throw_exception_ex(NULL, 0, "Unknown or bad format (%s)", format);
4161 retval = false;
4162 if (p) {
4163 timelib_rel_time_dtor(p);
4164 }
4165 } else {
4166 if(p) {
4167 *rt = p;
4168 retval = true;
4169 } else {
4170 if(b && e) {
4171 timelib_update_ts(b, NULL);
4172 timelib_update_ts(e, NULL);
4173 *rt = timelib_diff(b, e);
4174 retval = true;
4175 } else {
4176 zend_throw_exception_ex(NULL, 0, "Failed to parse interval (%s)", format);
4177 retval = false;
4178 }
4179 }
4180 }
4181 timelib_error_container_dtor(errors);
4182 timelib_free(b);
4183 timelib_free(e);
4184 return retval;
4185 } /* }}} */
4186
date_interval_compare_objects(zval * o1,zval * o2)4187 static int date_interval_compare_objects(zval *o1, zval *o2) {
4188 ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
4189 /* There is no well defined way to compare intervals like P1M and P30D, which may compare
4190 * smaller, equal or greater depending on the point in time at which the interval starts. As
4191 * such, we treat DateInterval objects are non-comparable and emit a warning. */
4192 zend_error(E_WARNING, "Cannot compare DateInterval objects");
4193 return ZEND_UNCOMPARABLE;
4194 }
4195
4196 /* {{{ date_interval_read_property */
date_interval_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)4197 static zval *date_interval_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
4198 {
4199 php_interval_obj *obj;
4200 zval *retval;
4201 timelib_sll value = -1;
4202 double fvalue = -1;
4203
4204 obj = php_interval_obj_from_obj(object);
4205
4206 if (!obj->initialized) {
4207 retval = zend_std_read_property(object, name, type, cache_slot, rv);
4208 return retval;
4209 }
4210
4211 #define GET_VALUE_FROM_STRUCT(n,m) \
4212 if (zend_string_equals_literal(name, m)) { \
4213 value = obj->diff->n; \
4214 break; \
4215 }
4216 do {
4217 GET_VALUE_FROM_STRUCT(y, "y");
4218 GET_VALUE_FROM_STRUCT(m, "m");
4219 GET_VALUE_FROM_STRUCT(d, "d");
4220 GET_VALUE_FROM_STRUCT(h, "h");
4221 GET_VALUE_FROM_STRUCT(i, "i");
4222 GET_VALUE_FROM_STRUCT(s, "s");
4223 if (zend_string_equals_literal(name, "f")) {
4224 fvalue = obj->diff->us / 1000000.0;
4225 break;
4226 }
4227 GET_VALUE_FROM_STRUCT(invert, "invert");
4228 GET_VALUE_FROM_STRUCT(days, "days");
4229 /* didn't find any */
4230 retval = zend_std_read_property(object, name, type, cache_slot, rv);
4231
4232 return retval;
4233 } while(0);
4234
4235 retval = rv;
4236
4237 if (fvalue != -1) {
4238 ZVAL_DOUBLE(retval, fvalue);
4239 } else if (value != TIMELIB_UNSET) {
4240 ZVAL_LONG(retval, value);
4241 } else {
4242 ZVAL_FALSE(retval);
4243 }
4244
4245 return retval;
4246 }
4247 /* }}} */
4248
4249 /* {{{ date_interval_write_property */
date_interval_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)4250 static zval *date_interval_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
4251 {
4252 php_interval_obj *obj;
4253
4254 obj = php_interval_obj_from_obj(object);
4255
4256 if (!obj->initialized) {
4257 return zend_std_write_property(object, name, value, cache_slot);
4258 }
4259
4260 #define SET_VALUE_FROM_STRUCT(n,m) \
4261 if (zend_string_equals_literal(name, m)) { \
4262 obj->diff->n = zval_get_long(value); \
4263 break; \
4264 }
4265
4266 do {
4267 SET_VALUE_FROM_STRUCT(y, "y");
4268 SET_VALUE_FROM_STRUCT(m, "m");
4269 SET_VALUE_FROM_STRUCT(d, "d");
4270 SET_VALUE_FROM_STRUCT(h, "h");
4271 SET_VALUE_FROM_STRUCT(i, "i");
4272 SET_VALUE_FROM_STRUCT(s, "s");
4273 if (zend_string_equals_literal(name, "f")) {
4274 obj->diff->us = zend_dval_to_lval(zval_get_double(value) * 1000000.0);
4275 break;
4276 }
4277 SET_VALUE_FROM_STRUCT(invert, "invert");
4278 /* didn't find any */
4279 value = zend_std_write_property(object, name, value, cache_slot);
4280 } while(0);
4281
4282 return value;
4283 }
4284 /* }}} */
4285
4286 /* {{{ date_interval_get_property_ptr_ptr */
date_interval_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)4287 static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
4288 {
4289 zval *ret;
4290
4291 if (
4292 zend_string_equals_literal(name, "y") ||
4293 zend_string_equals_literal(name, "m") ||
4294 zend_string_equals_literal(name, "d") ||
4295 zend_string_equals_literal(name, "h") ||
4296 zend_string_equals_literal(name, "i") ||
4297 zend_string_equals_literal(name, "s") ||
4298 zend_string_equals_literal(name, "f") ||
4299 zend_string_equals_literal(name, "days") ||
4300 zend_string_equals_literal(name, "invert") ) {
4301 /* Fallback to read_property. */
4302 ret = NULL;
4303 } else {
4304 ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
4305 }
4306
4307 return ret;
4308 }
4309 /* }}} */
4310
4311 /* {{{ Creates new DateInterval object. */
PHP_METHOD(DateInterval,__construct)4312 PHP_METHOD(DateInterval, __construct)
4313 {
4314 zend_string *interval_string = NULL;
4315 timelib_rel_time *reltime;
4316
4317 ZEND_PARSE_PARAMETERS_START(1, 1)
4318 Z_PARAM_STR(interval_string)
4319 ZEND_PARSE_PARAMETERS_END();
4320
4321 if (!date_interval_initialize(&reltime, ZSTR_VAL(interval_string), ZSTR_LEN(interval_string))) {
4322 RETURN_THROWS();
4323 }
4324
4325 php_interval_obj *diobj = Z_PHPINTERVAL_P(ZEND_THIS);
4326 diobj->diff = reltime;
4327 diobj->initialized = 1;
4328 diobj->civil_or_wall = PHP_DATE_WALL;
4329 }
4330 /* }}} */
4331
php_date_interval_initialize_from_hash(zval ** return_value,php_interval_obj ** intobj,HashTable * myht)4332 static void php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht) /* {{{ */
4333 {
4334 /* If ->diff is already set, then we need to free it first */
4335 if ((*intobj)->diff) {
4336 timelib_rel_time_dtor((*intobj)->diff);
4337 }
4338
4339 /* If we have a date_string, use that instead */
4340 zval *date_str = zend_hash_str_find(myht, "date_string", strlen("date_string"));
4341 if (date_str && Z_TYPE_P(date_str) == IS_STRING) {
4342 timelib_time *time;
4343 timelib_error_container *err = NULL;
4344
4345 time = timelib_strtotime(Z_STRVAL_P(date_str), Z_STRLEN_P(date_str), &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4346
4347 if (err->error_count > 0) {
4348 php_error_docref(NULL,
4349 E_WARNING,
4350 "Unknown or bad format (%s) at position %d (%c) while unserializing: %s",
4351 Z_STRVAL_P(date_str),
4352 err->error_messages[0].position,
4353 err->error_messages[0].character ? err->error_messages[0].character : ' ', err->error_messages[0].message);
4354 }
4355
4356 (*intobj)->diff = timelib_rel_time_clone(&time->relative);
4357 (*intobj)->initialized = 1;
4358 (*intobj)->civil_or_wall = PHP_DATE_CIVIL;
4359 (*intobj)->from_string = true;
4360 (*intobj)->date_string = zend_string_copy(Z_STR_P(date_str));
4361
4362 timelib_time_dtor(time);
4363 timelib_error_container_dtor(err);
4364
4365 return;
4366 }
4367
4368 /* Set new value */
4369 (*intobj)->diff = timelib_rel_time_ctor();
4370
4371 #define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \
4372 do { \
4373 zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
4374 if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
4375 (*intobj)->diff->member = (itype)zval_get_long(z_arg); \
4376 } else { \
4377 (*intobj)->diff->member = (itype)def; \
4378 } \
4379 } while (0);
4380
4381 #define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \
4382 do { \
4383 zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
4384 if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
4385 zend_string *tmp_str; \
4386 zend_string *str = zval_get_tmp_string(z_arg, &tmp_str); \
4387 DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
4388 zend_tmp_string_release(tmp_str); \
4389 } else { \
4390 (*intobj)->diff->member = -1LL; \
4391 } \
4392 } while (0);
4393
4394 #define PHP_DATE_INTERVAL_READ_PROPERTY_DAYS(member) \
4395 do { \
4396 zval *z_arg = zend_hash_str_find(myht, "days", sizeof("days") - 1); \
4397 if (z_arg && Z_TYPE_P(z_arg) == IS_FALSE) { \
4398 (*intobj)->diff->member = TIMELIB_UNSET; \
4399 } else if (z_arg && Z_TYPE_P(z_arg) <= IS_STRING) { \
4400 zend_string *str = zval_get_string(z_arg); \
4401 DATE_A64I((*intobj)->diff->member, ZSTR_VAL(str)); \
4402 zend_string_release(str); \
4403 } else { \
4404 (*intobj)->diff->member = -1LL; \
4405 } \
4406 } while (0);
4407
4408 #define PHP_DATE_INTERVAL_READ_PROPERTY_DOUBLE(element, member, def) \
4409 do { \
4410 zval *z_arg = zend_hash_str_find(myht, element, sizeof(element) - 1); \
4411 if (z_arg) { \
4412 (*intobj)->diff->member = (double)zval_get_double(z_arg); \
4413 } else { \
4414 (*intobj)->diff->member = (double)def; \
4415 } \
4416 } while (0);
4417
4418 PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1)
4419 PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1)
4420 PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1)
4421 PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1)
4422 PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1)
4423 PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1)
4424 {
4425 zval *z_arg = zend_hash_str_find(myht, "f", sizeof("f") - 1);
4426 if (z_arg) {
4427 (*intobj)->diff->us = zend_dval_to_lval(zval_get_double(z_arg) * 1000000.0);
4428 }
4429 }
4430 PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1)
4431 PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1)
4432 PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1)
4433 PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0);
4434 PHP_DATE_INTERVAL_READ_PROPERTY_DAYS(days);
4435 PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0);
4436 PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount);
4437 PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0);
4438 PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0);
4439 {
4440 zval *z_arg = zend_hash_str_find(myht, "civil_or_wall", sizeof("civil_or_wall") - 1);
4441 (*intobj)->civil_or_wall = PHP_DATE_CIVIL;
4442 if (z_arg) {
4443 zend_long val = zval_get_long(z_arg);
4444 (*intobj)->civil_or_wall = val;
4445 }
4446 }
4447
4448 (*intobj)->initialized = 1;
4449 } /* }}} */
4450
4451 /* {{{ */
PHP_METHOD(DateInterval,__set_state)4452 PHP_METHOD(DateInterval, __set_state)
4453 {
4454 php_interval_obj *intobj;
4455 zval *array;
4456 HashTable *myht;
4457
4458 ZEND_PARSE_PARAMETERS_START(1, 1)
4459 Z_PARAM_ARRAY(array)
4460 ZEND_PARSE_PARAMETERS_END();
4461
4462 myht = Z_ARRVAL_P(array);
4463
4464 php_date_instantiate(date_ce_interval, return_value);
4465 intobj = Z_PHPINTERVAL_P(return_value);
4466 php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4467 }
4468 /* }}} */
4469
4470 /* {{{ */
PHP_METHOD(DateInterval,__serialize)4471 PHP_METHOD(DateInterval, __serialize)
4472 {
4473 zval *object = ZEND_THIS;
4474 php_interval_obj *intervalobj;
4475 HashTable *myht;
4476
4477 ZEND_PARSE_PARAMETERS_NONE();
4478
4479 intervalobj = Z_PHPINTERVAL_P(object);
4480 DATE_CHECK_INITIALIZED(intervalobj->initialized, DateInterval);
4481
4482 array_init(return_value);
4483 myht = Z_ARRVAL_P(return_value);
4484 date_interval_object_to_hash(intervalobj, myht);
4485
4486 add_common_properties(myht, &intervalobj->std);
4487 }
4488 /* }}} */
4489
date_interval_is_internal_property(zend_string * name)4490 static bool date_interval_is_internal_property(zend_string *name)
4491 {
4492 if (
4493 zend_string_equals_literal(name, "date_string") ||
4494 zend_string_equals_literal(name, "from_string") ||
4495 zend_string_equals_literal(name, "y") ||
4496 zend_string_equals_literal(name, "m") ||
4497 zend_string_equals_literal(name, "d") ||
4498 zend_string_equals_literal(name, "h") ||
4499 zend_string_equals_literal(name, "i") ||
4500 zend_string_equals_literal(name, "s") ||
4501 zend_string_equals_literal(name, "f") ||
4502 zend_string_equals_literal(name, "invert") ||
4503 zend_string_equals_literal(name, "days")
4504 ) {
4505 return 1;
4506 }
4507 return 0;
4508 }
4509
restore_custom_dateinterval_properties(zval * object,HashTable * myht)4510 static void restore_custom_dateinterval_properties(zval *object, HashTable *myht)
4511 {
4512 zend_string *prop_name;
4513 zval *prop_val;
4514
4515 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
4516 if (!prop_name || (Z_TYPE_P(prop_val) == IS_REFERENCE) || date_interval_is_internal_property(prop_name)) {
4517 continue;
4518 }
4519 update_property(Z_OBJ_P(object), prop_name, prop_val);
4520 } ZEND_HASH_FOREACH_END();
4521 }
4522
4523
4524 /* {{{ */
PHP_METHOD(DateInterval,__unserialize)4525 PHP_METHOD(DateInterval, __unserialize)
4526 {
4527 zval *object = ZEND_THIS;
4528 php_interval_obj *intervalobj;
4529 zval *array;
4530 HashTable *myht;
4531
4532 ZEND_PARSE_PARAMETERS_START(1, 1)
4533 Z_PARAM_ARRAY(array)
4534 ZEND_PARSE_PARAMETERS_END();
4535
4536 intervalobj = Z_PHPINTERVAL_P(object);
4537 myht = Z_ARRVAL_P(array);
4538
4539 php_date_interval_initialize_from_hash(&object, &intervalobj, myht);
4540 restore_custom_dateinterval_properties(object, myht);
4541 }
4542 /* }}} */
4543
4544 /* {{{ */
PHP_METHOD(DateInterval,__wakeup)4545 PHP_METHOD(DateInterval, __wakeup)
4546 {
4547 zval *object = ZEND_THIS;
4548 php_interval_obj *intobj;
4549 HashTable *myht;
4550
4551 ZEND_PARSE_PARAMETERS_NONE();
4552
4553 intobj = Z_PHPINTERVAL_P(object);
4554
4555 myht = Z_OBJPROP_P(object);
4556
4557 php_date_interval_initialize_from_hash(&return_value, &intobj, myht);
4558 }
4559 /* }}} */
4560
4561 /* {{{ Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string */
PHP_FUNCTION(date_interval_create_from_date_string)4562 PHP_FUNCTION(date_interval_create_from_date_string)
4563 {
4564 zend_string *time_str = NULL;
4565 timelib_time *time;
4566 timelib_error_container *err = NULL;
4567 php_interval_obj *diobj;
4568
4569 ZEND_PARSE_PARAMETERS_START(1, 1)
4570 Z_PARAM_STR(time_str)
4571 ZEND_PARSE_PARAMETERS_END();
4572
4573 time = timelib_strtotime(ZSTR_VAL(time_str), ZSTR_LEN(time_str), &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
4574
4575 if (err->error_count > 0) {
4576 php_error_docref(NULL, E_WARNING, "Unknown or bad format (%s) at position %d (%c): %s", ZSTR_VAL(time_str),
4577 err->error_messages[0].position, err->error_messages[0].character ? err->error_messages[0].character : ' ', err->error_messages[0].message);
4578 RETVAL_FALSE;
4579 goto cleanup;
4580 }
4581
4582 if (time->have_date || time->have_time || time->have_zone) {
4583 php_error_docref(NULL, E_WARNING, "String '%s' contains non-relative elements", ZSTR_VAL(time_str));
4584 RETVAL_FALSE;
4585 goto cleanup;
4586 }
4587
4588 php_date_instantiate(date_ce_interval, return_value);
4589 diobj = Z_PHPINTERVAL_P(return_value);
4590 diobj->diff = timelib_rel_time_clone(&time->relative);
4591 diobj->initialized = 1;
4592 diobj->civil_or_wall = PHP_DATE_CIVIL;
4593 diobj->from_string = true;
4594 diobj->date_string = zend_string_copy(time_str);
4595
4596 cleanup:
4597 timelib_time_dtor(time);
4598 timelib_error_container_dtor(err);
4599 }
4600 /* }}} */
4601
4602 /* {{{ date_interval_format - */
date_interval_format(char * format,size_t format_len,timelib_rel_time * t)4603 static zend_string *date_interval_format(char *format, size_t format_len, timelib_rel_time *t)
4604 {
4605 smart_str string = {0};
4606 size_t i;
4607 int length, have_format_spec = 0;
4608 char buffer[33];
4609
4610 if (!format_len) {
4611 return ZSTR_EMPTY_ALLOC();
4612 }
4613
4614 for (i = 0; i < format_len; i++) {
4615 if (have_format_spec) {
4616 switch (format[i]) {
4617 case 'Y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->y); break;
4618 case 'y': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->y); break;
4619
4620 case 'M': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->m); break;
4621 case 'm': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->m); break;
4622
4623 case 'D': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->d); break;
4624 case 'd': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->d); break;
4625
4626 case 'H': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->h); break;
4627 case 'h': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->h); break;
4628
4629 case 'I': length = slprintf(buffer, sizeof(buffer), "%02d", (int) t->i); break;
4630 case 'i': length = slprintf(buffer, sizeof(buffer), "%d", (int) t->i); break;
4631
4632 case 'S': length = slprintf(buffer, sizeof(buffer), "%02" ZEND_LONG_FMT_SPEC, (zend_long) t->s); break;
4633 case 's': length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) t->s); break;
4634
4635 case 'F': length = slprintf(buffer, sizeof(buffer), "%06" ZEND_LONG_FMT_SPEC, (zend_long) t->us); break;
4636 case 'f': length = slprintf(buffer, sizeof(buffer), ZEND_LONG_FMT, (zend_long) t->us); break;
4637
4638 case 'a': {
4639 if ((int) t->days != TIMELIB_UNSET) {
4640 length = slprintf(buffer, sizeof(buffer), "%d", (int) t->days);
4641 } else {
4642 length = slprintf(buffer, sizeof(buffer), "(unknown)");
4643 }
4644 } break;
4645 case 'r': length = slprintf(buffer, sizeof(buffer), "%s", t->invert ? "-" : ""); break;
4646 case 'R': length = slprintf(buffer, sizeof(buffer), "%c", t->invert ? '-' : '+'); break;
4647
4648 case '%': length = slprintf(buffer, sizeof(buffer), "%%"); break;
4649 default: buffer[0] = '%'; buffer[1] = format[i]; buffer[2] = '\0'; length = 2; break;
4650 }
4651 smart_str_appendl(&string, buffer, length);
4652 have_format_spec = 0;
4653 } else {
4654 if (format[i] == '%') {
4655 have_format_spec = 1;
4656 } else {
4657 smart_str_appendc(&string, format[i]);
4658 }
4659 }
4660 }
4661
4662 smart_str_0(&string);
4663
4664 if (string.s == NULL) {
4665 return ZSTR_EMPTY_ALLOC();
4666 }
4667
4668 return string.s;
4669 }
4670 /* }}} */
4671
4672 /* {{{ Formats the interval. */
PHP_FUNCTION(date_interval_format)4673 PHP_FUNCTION(date_interval_format)
4674 {
4675 zval *object;
4676 php_interval_obj *diobj;
4677 char *format;
4678 size_t format_len;
4679
4680 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &object, date_ce_interval, &format, &format_len) == FAILURE) {
4681 RETURN_THROWS();
4682 }
4683 diobj = Z_PHPINTERVAL_P(object);
4684 DATE_CHECK_INITIALIZED(diobj->initialized, DateInterval);
4685
4686 RETURN_STR(date_interval_format(format, format_len, diobj->diff));
4687 }
4688 /* }}} */
4689
date_period_initialize(timelib_time ** st,timelib_time ** et,timelib_rel_time ** d,zend_long * recurrences,char * format,size_t format_length)4690 static bool date_period_initialize(timelib_time **st, timelib_time **et, timelib_rel_time **d, zend_long *recurrences, /*const*/ char *format, size_t format_length) /* {{{ */
4691 {
4692 timelib_time *b = NULL, *e = NULL;
4693 timelib_rel_time *p = NULL;
4694 int r = 0;
4695 timelib_error_container *errors;
4696 bool retval = false;
4697
4698 timelib_strtointerval(format, format_length, &b, &e, &p, &r, &errors);
4699
4700 if (errors->error_count > 0) {
4701 retval = false;
4702 zend_throw_exception_ex(NULL, 0, "Unknown or bad format (%s)", format);
4703 if (b) {
4704 timelib_time_dtor(b);
4705 }
4706 if (e) {
4707 timelib_time_dtor(e);
4708 }
4709 if (p) {
4710 timelib_rel_time_dtor(p);
4711 }
4712 } else {
4713 *st = b;
4714 *et = e;
4715 *d = p;
4716 *recurrences = r;
4717 retval = true;
4718 }
4719 timelib_error_container_dtor(errors);
4720 return retval;
4721 } /* }}} */
4722
4723 /* {{{ Creates new DatePeriod object. */
PHP_METHOD(DatePeriod,__construct)4724 PHP_METHOD(DatePeriod, __construct)
4725 {
4726 php_period_obj *dpobj;
4727 php_date_obj *dateobj;
4728 zval *start, *end = NULL, *interval;
4729 zend_long recurrences = 0, options = 0;
4730 char *isostr = NULL;
4731 size_t isostr_len = 0;
4732 timelib_time *clone;
4733
4734 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOl|l", &start, date_ce_interface, &interval, date_ce_interval, &recurrences, &options) == FAILURE) {
4735 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_interface, &options) == FAILURE) {
4736 if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|l", &isostr, &isostr_len, &options) == FAILURE) {
4737 zend_type_error("DatePeriod::__construct() accepts (DateTimeInterface, DateInterval, int [, int]), or (DateTimeInterface, DateInterval, DateTime [, int]), or (string [, int]) as arguments");
4738 RETURN_THROWS();
4739 }
4740 }
4741 }
4742
4743 dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4744 dpobj->current = NULL;
4745
4746 if (isostr) {
4747 if (!date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len)) {
4748 RETURN_THROWS();
4749 }
4750
4751 if (dpobj->start == NULL) {
4752 zend_string *func = get_active_function_or_method_name();
4753 zend_throw_exception_ex(NULL, 0, "%s(): ISO interval must contain a start date, \"%s\" given", ZSTR_VAL(func), isostr);
4754 zend_string_release(func);
4755 RETURN_THROWS();
4756 }
4757 if (dpobj->interval == NULL) {
4758 zend_string *func = get_active_function_or_method_name();
4759 zend_throw_exception_ex(NULL, 0, "%s(): ISO interval must contain an interval, \"%s\" given", ZSTR_VAL(func), isostr);
4760 zend_string_release(func);
4761 RETURN_THROWS();
4762 }
4763 if (dpobj->end == NULL && recurrences == 0) {
4764 zend_string *func = get_active_function_or_method_name();
4765 zend_throw_exception_ex(NULL, 0, "%s(): ISO interval must contain an end date or a recurrence count, \"%s\" given", ZSTR_VAL(func), isostr);
4766 zend_string_release(func);
4767 RETURN_THROWS();
4768 }
4769
4770 if (dpobj->start) {
4771 timelib_update_ts(dpobj->start, NULL);
4772 }
4773 if (dpobj->end) {
4774 timelib_update_ts(dpobj->end, NULL);
4775 }
4776 dpobj->start_ce = date_ce_date;
4777 } else {
4778 /* check initialisation */
4779 DATE_CHECK_INITIALIZED(Z_PHPDATE_P(start)->time, DateTimeInterface);
4780 if (end) {
4781 DATE_CHECK_INITIALIZED(Z_PHPDATE_P(end)->time, DateTimeInterface);
4782 }
4783
4784 /* init */
4785 php_interval_obj *intobj = Z_PHPINTERVAL_P(interval);
4786
4787 /* start date */
4788 dateobj = Z_PHPDATE_P(start);
4789 clone = timelib_time_ctor();
4790 memcpy(clone, dateobj->time, sizeof(timelib_time));
4791 if (dateobj->time->tz_abbr) {
4792 clone->tz_abbr = timelib_strdup(dateobj->time->tz_abbr);
4793 }
4794 if (dateobj->time->tz_info) {
4795 clone->tz_info = dateobj->time->tz_info;
4796 }
4797 dpobj->start = clone;
4798 dpobj->start_ce = Z_OBJCE_P(start);
4799
4800 /* interval */
4801 dpobj->interval = timelib_rel_time_clone(intobj->diff);
4802
4803 /* end date */
4804 if (end) {
4805 dateobj = Z_PHPDATE_P(end);
4806 clone = timelib_time_clone(dateobj->time);
4807 dpobj->end = clone;
4808 }
4809 }
4810
4811 if (dpobj->end == NULL && recurrences < 1) {
4812 zend_string *func = get_active_function_or_method_name();
4813 zend_throw_exception_ex(NULL, 0, "%s(): Recurrence count must be greater than 0", ZSTR_VAL(func));
4814 zend_string_release(func);
4815 RETURN_THROWS();
4816 }
4817
4818 /* options */
4819 dpobj->include_start_date = !(options & PHP_DATE_PERIOD_EXCLUDE_START_DATE);
4820 dpobj->include_end_date = options & PHP_DATE_PERIOD_INCLUDE_END_DATE;
4821
4822 /* recurrrences */
4823 dpobj->recurrences = recurrences + dpobj->include_start_date + dpobj->include_end_date;
4824
4825 dpobj->initialized = 1;
4826
4827 initialize_date_period_properties(dpobj);
4828 }
4829 /* }}} */
4830
4831 /* {{{ Get start date. */
PHP_METHOD(DatePeriod,getStartDate)4832 PHP_METHOD(DatePeriod, getStartDate)
4833 {
4834 php_period_obj *dpobj;
4835 php_date_obj *dateobj;
4836
4837 ZEND_PARSE_PARAMETERS_NONE();
4838
4839 dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4840 DATE_CHECK_INITIALIZED(dpobj->start, DatePeriod);
4841
4842 php_date_instantiate(dpobj->start_ce, return_value);
4843 dateobj = Z_PHPDATE_P(return_value);
4844 dateobj->time = timelib_time_ctor();
4845 *dateobj->time = *dpobj->start;
4846 if (dpobj->start->tz_abbr) {
4847 dateobj->time->tz_abbr = timelib_strdup(dpobj->start->tz_abbr);
4848 }
4849 if (dpobj->start->tz_info) {
4850 dateobj->time->tz_info = dpobj->start->tz_info;
4851 }
4852 }
4853 /* }}} */
4854
4855 /* {{{ Get end date. */
PHP_METHOD(DatePeriod,getEndDate)4856 PHP_METHOD(DatePeriod, getEndDate)
4857 {
4858 php_period_obj *dpobj;
4859 php_date_obj *dateobj;
4860
4861 ZEND_PARSE_PARAMETERS_NONE();
4862
4863 dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4864
4865 if (!dpobj->end) {
4866 return;
4867 }
4868
4869 php_date_instantiate(dpobj->start_ce, return_value);
4870 dateobj = Z_PHPDATE_P(return_value);
4871 dateobj->time = timelib_time_ctor();
4872 *dateobj->time = *dpobj->end;
4873 if (dpobj->end->tz_abbr) {
4874 dateobj->time->tz_abbr = timelib_strdup(dpobj->end->tz_abbr);
4875 }
4876 if (dpobj->end->tz_info) {
4877 dateobj->time->tz_info = dpobj->end->tz_info;
4878 }
4879 }
4880 /* }}} */
4881
4882 /* {{{ Get date interval. */
PHP_METHOD(DatePeriod,getDateInterval)4883 PHP_METHOD(DatePeriod, getDateInterval)
4884 {
4885 php_period_obj *dpobj;
4886 php_interval_obj *diobj;
4887
4888 ZEND_PARSE_PARAMETERS_NONE();
4889
4890 dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4891 DATE_CHECK_INITIALIZED(dpobj->interval, DatePeriod);
4892
4893 php_date_instantiate(date_ce_interval, return_value);
4894 diobj = Z_PHPINTERVAL_P(return_value);
4895 diobj->diff = timelib_rel_time_clone(dpobj->interval);
4896 diobj->initialized = 1;
4897 }
4898 /* }}} */
4899
4900 /* {{{ Get recurrences. */
PHP_METHOD(DatePeriod,getRecurrences)4901 PHP_METHOD(DatePeriod, getRecurrences)
4902 {
4903 php_period_obj *dpobj;
4904
4905 ZEND_PARSE_PARAMETERS_NONE();
4906
4907 dpobj = Z_PHPPERIOD_P(ZEND_THIS);
4908
4909 if (0 == dpobj->recurrences - dpobj->include_start_date - dpobj->include_end_date) {
4910 return;
4911 }
4912
4913 RETURN_LONG(dpobj->recurrences - dpobj->include_start_date - dpobj->include_end_date);
4914 }
4915 /* }}} */
4916
PHP_METHOD(DatePeriod,getIterator)4917 PHP_METHOD(DatePeriod, getIterator)
4918 {
4919 ZEND_PARSE_PARAMETERS_NONE();
4920
4921 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
4922 }
4923
check_id_allowed(char * id,zend_long what)4924 static int check_id_allowed(char *id, zend_long what) /* {{{ */
4925 {
4926 if ((what & PHP_DATE_TIMEZONE_GROUP_AFRICA) && strncasecmp(id, "Africa/", 7) == 0) return 1;
4927 if ((what & PHP_DATE_TIMEZONE_GROUP_AMERICA) && strncasecmp(id, "America/", 8) == 0) return 1;
4928 if ((what & PHP_DATE_TIMEZONE_GROUP_ANTARCTICA) && strncasecmp(id, "Antarctica/", 11) == 0) return 1;
4929 if ((what & PHP_DATE_TIMEZONE_GROUP_ARCTIC) && strncasecmp(id, "Arctic/", 7) == 0) return 1;
4930 if ((what & PHP_DATE_TIMEZONE_GROUP_ASIA) && strncasecmp(id, "Asia/", 5) == 0) return 1;
4931 if ((what & PHP_DATE_TIMEZONE_GROUP_ATLANTIC) && strncasecmp(id, "Atlantic/", 9) == 0) return 1;
4932 if ((what & PHP_DATE_TIMEZONE_GROUP_AUSTRALIA) && strncasecmp(id, "Australia/", 10) == 0) return 1;
4933 if ((what & PHP_DATE_TIMEZONE_GROUP_EUROPE) && strncasecmp(id, "Europe/", 7) == 0) return 1;
4934 if ((what & PHP_DATE_TIMEZONE_GROUP_INDIAN) && strncasecmp(id, "Indian/", 7) == 0) return 1;
4935 if ((what & PHP_DATE_TIMEZONE_GROUP_PACIFIC) && strncasecmp(id, "Pacific/", 8) == 0) return 1;
4936 if ((what & PHP_DATE_TIMEZONE_GROUP_UTC) && strncasecmp(id, "UTC", 3) == 0) return 1;
4937 return 0;
4938 } /* }}} */
4939
4940 /* {{{ Returns numerically index array with all timezone identifiers. */
PHP_FUNCTION(timezone_identifiers_list)4941 PHP_FUNCTION(timezone_identifiers_list)
4942 {
4943 const timelib_tzdb *tzdb;
4944 const timelib_tzdb_index_entry *table;
4945 int i, item_count;
4946 zend_long what = PHP_DATE_TIMEZONE_GROUP_ALL;
4947 char *option = NULL;
4948 size_t option_len = 0;
4949
4950 ZEND_PARSE_PARAMETERS_START(0, 2)
4951 Z_PARAM_OPTIONAL
4952 Z_PARAM_LONG(what)
4953 Z_PARAM_STRING_OR_NULL(option, option_len)
4954 ZEND_PARSE_PARAMETERS_END();
4955
4956 /* Extra validation */
4957 if (what == PHP_DATE_TIMEZONE_PER_COUNTRY && option_len != 2) {
4958 zend_argument_value_error(2, "must be a two-letter ISO 3166-1 compatible country code "
4959 "when argument #1 ($timezoneGroup) is DateTimeZone::PER_COUNTRY");
4960 RETURN_THROWS();
4961 }
4962
4963 tzdb = DATE_TIMEZONEDB;
4964 table = timelib_timezone_identifiers_list((timelib_tzdb*) tzdb, &item_count);
4965
4966 array_init(return_value);
4967
4968 for (i = 0; i < item_count; ++i) {
4969 if (what == PHP_DATE_TIMEZONE_PER_COUNTRY) {
4970 if (tzdb->data[table[i].pos + 5] == option[0] && tzdb->data[table[i].pos + 6] == option[1]) {
4971 add_next_index_string(return_value, table[i].id);
4972 }
4973 } else if (what == PHP_DATE_TIMEZONE_GROUP_ALL_W_BC || (check_id_allowed(table[i].id, what) && (tzdb->data[table[i].pos + 4] == '\1'))) {
4974 add_next_index_string(return_value, table[i].id);
4975 }
4976 };
4977 }
4978 /* }}} */
4979
4980 /* {{{ Returns the Olson database version number. */
PHP_FUNCTION(timezone_version_get)4981 PHP_FUNCTION(timezone_version_get)
4982 {
4983 const timelib_tzdb *tzdb;
4984
4985 ZEND_PARSE_PARAMETERS_NONE();
4986
4987 tzdb = DATE_TIMEZONEDB;
4988 RETURN_STRING(tzdb->version);
4989 }
4990 /* }}} */
4991
4992 /* {{{ Returns associative array containing dst, offset and the timezone name */
PHP_FUNCTION(timezone_abbreviations_list)4993 PHP_FUNCTION(timezone_abbreviations_list)
4994 {
4995 const timelib_tz_lookup_table *table, *entry;
4996 zval element, *abbr_array_p, abbr_array;
4997
4998 ZEND_PARSE_PARAMETERS_NONE();
4999
5000 table = timelib_timezone_abbreviations_list();
5001 array_init(return_value);
5002 entry = table;
5003
5004 do {
5005 array_init(&element);
5006 add_assoc_bool_ex(&element, "dst", sizeof("dst") -1, entry->type);
5007 add_assoc_long_ex(&element, "offset", sizeof("offset") - 1, entry->gmtoffset);
5008 if (entry->full_tz_name) {
5009 add_assoc_string_ex(&element, "timezone_id", sizeof("timezone_id") - 1, entry->full_tz_name);
5010 } else {
5011 add_assoc_null_ex(&element, "timezone_id", sizeof("timezone_id") - 1);
5012 }
5013
5014 abbr_array_p = zend_hash_str_find(Z_ARRVAL_P(return_value), entry->name, strlen(entry->name));
5015 if (!abbr_array_p) {
5016 array_init(&abbr_array);
5017 add_assoc_zval(return_value, entry->name, &abbr_array);
5018 } else {
5019 ZVAL_COPY_VALUE(&abbr_array, abbr_array_p);
5020 }
5021 add_next_index_zval(&abbr_array, &element);
5022 entry++;
5023 } while (entry->name);
5024 }
5025 /* }}} */
5026
5027 /* {{{ Sets the default timezone used by all date/time functions in a script */
PHP_FUNCTION(date_default_timezone_set)5028 PHP_FUNCTION(date_default_timezone_set)
5029 {
5030 char *zone;
5031 size_t zone_len;
5032
5033 ZEND_PARSE_PARAMETERS_START(1, 1)
5034 Z_PARAM_STRING(zone, zone_len)
5035 ZEND_PARSE_PARAMETERS_END();
5036
5037 if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
5038 php_error_docref(NULL, E_NOTICE, "Timezone ID '%s' is invalid", zone);
5039 RETURN_FALSE;
5040 }
5041 if (DATEG(timezone)) {
5042 efree(DATEG(timezone));
5043 DATEG(timezone) = NULL;
5044 }
5045 DATEG(timezone) = estrndup(zone, zone_len);
5046 RETURN_TRUE;
5047 }
5048 /* }}} */
5049
5050 /* {{{ Gets the default timezone used by all date/time functions in a script */
PHP_FUNCTION(date_default_timezone_get)5051 PHP_FUNCTION(date_default_timezone_get)
5052 {
5053 timelib_tzinfo *default_tz;
5054 ZEND_PARSE_PARAMETERS_NONE();
5055
5056 default_tz = get_timezone_info();
5057 if (!default_tz) {
5058 RETURN_THROWS();
5059 }
5060 RETVAL_STRING(default_tz->name);
5061 }
5062 /* }}} */
5063
5064 /* {{{ php_do_date_sunrise_sunset
5065 * Common for date_sunrise() and date_sunset() functions
5066 */
php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS,bool calc_sunset)5067 static void php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAMETERS, bool calc_sunset)
5068 {
5069 double latitude, longitude, zenith, gmt_offset, altitude;
5070 bool latitude_is_null = 1, longitude_is_null = 1, zenith_is_null = 1, gmt_offset_is_null = 1;
5071 double h_rise, h_set, N;
5072 timelib_sll rise, set, transit;
5073 zend_long time, retformat = SUNFUNCS_RET_STRING;
5074 int rs;
5075 timelib_time *t;
5076 timelib_tzinfo *tzi;
5077 zend_string *retstr;
5078
5079 ZEND_PARSE_PARAMETERS_START(1, 6)
5080 Z_PARAM_LONG(time)
5081 Z_PARAM_OPTIONAL
5082 Z_PARAM_LONG(retformat)
5083 Z_PARAM_DOUBLE_OR_NULL(latitude, latitude_is_null)
5084 Z_PARAM_DOUBLE_OR_NULL(longitude, longitude_is_null)
5085 Z_PARAM_DOUBLE_OR_NULL(zenith, zenith_is_null)
5086 Z_PARAM_DOUBLE_OR_NULL(gmt_offset, gmt_offset_is_null)
5087 ZEND_PARSE_PARAMETERS_END();
5088
5089 if (latitude_is_null) {
5090 latitude = INI_FLT("date.default_latitude");
5091 }
5092
5093 if (longitude_is_null) {
5094 longitude = INI_FLT("date.default_longitude");
5095 }
5096
5097 if (zenith_is_null) {
5098 if (calc_sunset) {
5099 zenith = INI_FLT("date.sunset_zenith");
5100 } else {
5101 zenith = INI_FLT("date.sunrise_zenith");
5102 }
5103 }
5104
5105 if (retformat != SUNFUNCS_RET_TIMESTAMP &&
5106 retformat != SUNFUNCS_RET_STRING &&
5107 retformat != SUNFUNCS_RET_DOUBLE)
5108 {
5109 zend_argument_value_error(2, "must be one of SUNFUNCS_RET_TIMESTAMP, SUNFUNCS_RET_STRING, or SUNFUNCS_RET_DOUBLE");
5110 RETURN_THROWS();
5111 }
5112 altitude = 90 - zenith;
5113
5114 /* Initialize time struct */
5115 tzi = get_timezone_info();
5116 if (!tzi) {
5117 RETURN_THROWS();
5118 }
5119 t = timelib_time_ctor();
5120 t->tz_info = tzi;
5121 t->zone_type = TIMELIB_ZONETYPE_ID;
5122
5123 if (gmt_offset_is_null) {
5124 gmt_offset = timelib_get_current_offset(t) / 3600;
5125 }
5126
5127 timelib_unixtime2local(t, time);
5128 rs = timelib_astro_rise_set_altitude(t, longitude, latitude, altitude, 1, &h_rise, &h_set, &rise, &set, &transit);
5129 timelib_time_dtor(t);
5130
5131 if (rs != 0) {
5132 RETURN_FALSE;
5133 }
5134
5135 if (retformat == SUNFUNCS_RET_TIMESTAMP) {
5136 RETURN_LONG(calc_sunset ? set : rise);
5137 }
5138 N = (calc_sunset ? h_set : h_rise) + gmt_offset;
5139
5140 if (N > 24 || N < 0) {
5141 N -= floor(N / 24) * 24;
5142 }
5143
5144 switch (retformat) {
5145 case SUNFUNCS_RET_STRING:
5146 retstr = strpprintf(0, "%02d:%02d", (int) N, (int) (60 * (N - (int) N)));
5147 RETURN_NEW_STR(retstr);
5148 break;
5149 case SUNFUNCS_RET_DOUBLE:
5150 RETURN_DOUBLE(N);
5151 break;
5152 }
5153 }
5154 /* }}} */
5155
5156 /* {{{ Returns time of sunrise for a given day and location */
PHP_FUNCTION(date_sunrise)5157 PHP_FUNCTION(date_sunrise)
5158 {
5159 php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
5160 }
5161 /* }}} */
5162
5163 /* {{{ Returns time of sunset for a given day and location */
PHP_FUNCTION(date_sunset)5164 PHP_FUNCTION(date_sunset)
5165 {
5166 php_do_date_sunrise_sunset(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
5167 }
5168 /* }}} */
5169
5170 /* {{{ Returns an array with information about sun set/rise and twilight begin/end */
PHP_FUNCTION(date_sun_info)5171 PHP_FUNCTION(date_sun_info)
5172 {
5173 zend_long time;
5174 double latitude, longitude;
5175 timelib_time *t, *t2;
5176 timelib_tzinfo *tzi;
5177 int rs;
5178 timelib_sll rise, set, transit;
5179 int dummy;
5180 double ddummy;
5181
5182 ZEND_PARSE_PARAMETERS_START(3, 3)
5183 Z_PARAM_LONG(time)
5184 Z_PARAM_DOUBLE(latitude)
5185 Z_PARAM_DOUBLE(longitude)
5186 ZEND_PARSE_PARAMETERS_END();
5187
5188 /* Initialize time struct */
5189 tzi = get_timezone_info();
5190 if (!tzi) {
5191 RETURN_THROWS();
5192 }
5193 t = timelib_time_ctor();
5194 t->tz_info = tzi;
5195 t->zone_type = TIMELIB_ZONETYPE_ID;
5196 timelib_unixtime2local(t, time);
5197
5198 /* Setup */
5199 t2 = timelib_time_ctor();
5200 array_init(return_value);
5201
5202 /* Get sun up/down and transit */
5203 rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -50.0/60, 1, &ddummy, &ddummy, &rise, &set, &transit);
5204 switch (rs) {
5205 case -1: /* always below */
5206 add_assoc_bool(return_value, "sunrise", 0);
5207 add_assoc_bool(return_value, "sunset", 0);
5208 break;
5209 case 1: /* always above */
5210 add_assoc_bool(return_value, "sunrise", 1);
5211 add_assoc_bool(return_value, "sunset", 1);
5212 break;
5213 default:
5214 t2->sse = rise;
5215 add_assoc_long(return_value, "sunrise", timelib_date_to_int(t2, &dummy));
5216 t2->sse = set;
5217 add_assoc_long(return_value, "sunset", timelib_date_to_int(t2, &dummy));
5218 }
5219 t2->sse = transit;
5220 add_assoc_long(return_value, "transit", timelib_date_to_int(t2, &dummy));
5221
5222 /* Get civil twilight */
5223 rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -6.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
5224 switch (rs) {
5225 case -1: /* always below */
5226 add_assoc_bool(return_value, "civil_twilight_begin", 0);
5227 add_assoc_bool(return_value, "civil_twilight_end", 0);
5228 break;
5229 case 1: /* always above */
5230 add_assoc_bool(return_value, "civil_twilight_begin", 1);
5231 add_assoc_bool(return_value, "civil_twilight_end", 1);
5232 break;
5233 default:
5234 t2->sse = rise;
5235 add_assoc_long(return_value, "civil_twilight_begin", timelib_date_to_int(t2, &dummy));
5236 t2->sse = set;
5237 add_assoc_long(return_value, "civil_twilight_end", timelib_date_to_int(t2, &dummy));
5238 }
5239
5240 /* Get nautical twilight */
5241 rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -12.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
5242 switch (rs) {
5243 case -1: /* always below */
5244 add_assoc_bool(return_value, "nautical_twilight_begin", 0);
5245 add_assoc_bool(return_value, "nautical_twilight_end", 0);
5246 break;
5247 case 1: /* always above */
5248 add_assoc_bool(return_value, "nautical_twilight_begin", 1);
5249 add_assoc_bool(return_value, "nautical_twilight_end", 1);
5250 break;
5251 default:
5252 t2->sse = rise;
5253 add_assoc_long(return_value, "nautical_twilight_begin", timelib_date_to_int(t2, &dummy));
5254 t2->sse = set;
5255 add_assoc_long(return_value, "nautical_twilight_end", timelib_date_to_int(t2, &dummy));
5256 }
5257
5258 /* Get astronomical twilight */
5259 rs = timelib_astro_rise_set_altitude(t, longitude, latitude, -18.0, 0, &ddummy, &ddummy, &rise, &set, &transit);
5260 switch (rs) {
5261 case -1: /* always below */
5262 add_assoc_bool(return_value, "astronomical_twilight_begin", 0);
5263 add_assoc_bool(return_value, "astronomical_twilight_end", 0);
5264 break;
5265 case 1: /* always above */
5266 add_assoc_bool(return_value, "astronomical_twilight_begin", 1);
5267 add_assoc_bool(return_value, "astronomical_twilight_end", 1);
5268 break;
5269 default:
5270 t2->sse = rise;
5271 add_assoc_long(return_value, "astronomical_twilight_begin", timelib_date_to_int(t2, &dummy));
5272 t2->sse = set;
5273 add_assoc_long(return_value, "astronomical_twilight_end", timelib_date_to_int(t2, &dummy));
5274 }
5275 timelib_time_dtor(t);
5276 timelib_time_dtor(t2);
5277 }
5278 /* }}} */
5279
date_object_get_gc_period(zend_object * object,zval ** table,int * n)5280 static HashTable *date_object_get_gc_period(zend_object *object, zval **table, int *n) /* {{{ */
5281 {
5282 *table = NULL;
5283 *n = 0;
5284 return zend_std_get_properties(object);
5285 } /* }}} */
5286
date_period_object_to_hash(php_period_obj * period_obj,HashTable * props)5287 static void date_period_object_to_hash(php_period_obj *period_obj, HashTable *props)
5288 {
5289 zval zv;
5290
5291 create_date_period_datetime(period_obj->start, period_obj->start_ce, &zv);
5292 zend_hash_str_update(props, "start", sizeof("start")-1, &zv);
5293
5294 create_date_period_datetime(period_obj->current, period_obj->start_ce, &zv);
5295 zend_hash_str_update(props, "current", sizeof("current")-1, &zv);
5296
5297 create_date_period_datetime(period_obj->end, period_obj->start_ce, &zv);
5298 zend_hash_str_update(props, "end", sizeof("end")-1, &zv);
5299
5300 create_date_period_interval(period_obj->interval, &zv);
5301 zend_hash_str_update(props, "interval", sizeof("interval")-1, &zv);
5302
5303 /* converted to larger type (int->long); must check when unserializing */
5304 ZVAL_LONG(&zv, (zend_long) period_obj->recurrences);
5305 zend_hash_str_update(props, "recurrences", sizeof("recurrences")-1, &zv);
5306
5307 ZVAL_BOOL(&zv, period_obj->include_start_date);
5308 zend_hash_str_update(props, "include_start_date", sizeof("include_start_date")-1, &zv);
5309
5310 ZVAL_BOOL(&zv, period_obj->include_end_date);
5311 zend_hash_str_update(props, "include_end_date", sizeof("include_end_date")-1, &zv);
5312 }
5313
php_date_period_initialize_from_hash(php_period_obj * period_obj,HashTable * myht)5314 static bool php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht) /* {{{ */
5315 {
5316 zval *ht_entry;
5317
5318 /* this function does no rollback on error */
5319
5320 ht_entry = zend_hash_str_find(myht, "start", sizeof("start")-1);
5321 if (ht_entry) {
5322 if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
5323 php_date_obj *date_obj;
5324 date_obj = Z_PHPDATE_P(ht_entry);
5325
5326 if (!date_obj->time) {
5327 return 0;
5328 }
5329
5330 if (period_obj->start != NULL) {
5331 timelib_time_dtor(period_obj->start);
5332 }
5333 period_obj->start = timelib_time_clone(date_obj->time);
5334 period_obj->start_ce = Z_OBJCE_P(ht_entry);
5335 } else if (Z_TYPE_P(ht_entry) != IS_NULL) {
5336 return 0;
5337 }
5338 } else {
5339 return 0;
5340 }
5341
5342 ht_entry = zend_hash_str_find(myht, "end", sizeof("end")-1);
5343 if (ht_entry) {
5344 if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
5345 php_date_obj *date_obj;
5346 date_obj = Z_PHPDATE_P(ht_entry);
5347
5348 if (!date_obj->time) {
5349 return 0;
5350 }
5351
5352 if (period_obj->end != NULL) {
5353 timelib_time_dtor(period_obj->end);
5354 }
5355 period_obj->end = timelib_time_clone(date_obj->time);
5356 } else if (Z_TYPE_P(ht_entry) != IS_NULL) {
5357 return 0;
5358 }
5359 } else {
5360 return 0;
5361 }
5362
5363 ht_entry = zend_hash_str_find(myht, "current", sizeof("current")-1);
5364 if (ht_entry) {
5365 if (Z_TYPE_P(ht_entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(ht_entry), date_ce_interface)) {
5366 php_date_obj *date_obj;
5367 date_obj = Z_PHPDATE_P(ht_entry);
5368
5369 if (!date_obj->time) {
5370 return 0;
5371 }
5372
5373 if (period_obj->current != NULL) {
5374 timelib_time_dtor(period_obj->current);
5375 }
5376 period_obj->current = timelib_time_clone(date_obj->time);
5377 } else if (Z_TYPE_P(ht_entry) != IS_NULL) {
5378 return 0;
5379 }
5380 } else {
5381 return 0;
5382 }
5383
5384 ht_entry = zend_hash_str_find(myht, "interval", sizeof("interval")-1);
5385 if (ht_entry) {
5386 if (Z_TYPE_P(ht_entry) == IS_OBJECT && Z_OBJCE_P(ht_entry) == date_ce_interval) {
5387 php_interval_obj *interval_obj;
5388 interval_obj = Z_PHPINTERVAL_P(ht_entry);
5389
5390 if (!interval_obj->initialized) {
5391 return 0;
5392 }
5393
5394 if (period_obj->interval != NULL) {
5395 timelib_rel_time_dtor(period_obj->interval);
5396 }
5397 period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
5398 } else { /* interval is required */
5399 return 0;
5400 }
5401 } else {
5402 return 0;
5403 }
5404
5405 ht_entry = zend_hash_str_find(myht, "recurrences", sizeof("recurrences")-1);
5406 if (ht_entry &&
5407 Z_TYPE_P(ht_entry) == IS_LONG && Z_LVAL_P(ht_entry) >= 0 && Z_LVAL_P(ht_entry) <= INT_MAX) {
5408 period_obj->recurrences = Z_LVAL_P(ht_entry);
5409 } else {
5410 return 0;
5411 }
5412
5413 ht_entry = zend_hash_str_find(myht, "include_start_date", sizeof("include_start_date")-1);
5414 if (ht_entry &&
5415 (Z_TYPE_P(ht_entry) == IS_FALSE || Z_TYPE_P(ht_entry) == IS_TRUE)) {
5416 period_obj->include_start_date = (Z_TYPE_P(ht_entry) == IS_TRUE);
5417 } else {
5418 return 0;
5419 }
5420
5421 ht_entry = zend_hash_str_find(myht, "include_end_date", sizeof("include_end_date")-1);
5422 if (ht_entry &&
5423 (Z_TYPE_P(ht_entry) == IS_FALSE || Z_TYPE_P(ht_entry) == IS_TRUE)) {
5424 period_obj->include_end_date = (Z_TYPE_P(ht_entry) == IS_TRUE);
5425 } else {
5426 return 0;
5427 }
5428
5429 period_obj->initialized = 1;
5430
5431 initialize_date_period_properties(period_obj);
5432
5433 return 1;
5434 } /* }}} */
5435
5436 /* {{{ */
PHP_METHOD(DatePeriod,__set_state)5437 PHP_METHOD(DatePeriod, __set_state)
5438 {
5439 php_period_obj *period_obj;
5440 zval *array;
5441 HashTable *myht;
5442
5443 ZEND_PARSE_PARAMETERS_START(1, 1)
5444 Z_PARAM_ARRAY(array)
5445 ZEND_PARSE_PARAMETERS_END();
5446
5447 myht = Z_ARRVAL_P(array);
5448
5449 object_init_ex(return_value, date_ce_period);
5450 period_obj = Z_PHPPERIOD_P(return_value);
5451 if (!php_date_period_initialize_from_hash(period_obj, myht)) {
5452 zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
5453 RETURN_THROWS();
5454 }
5455 }
5456 /* }}} */
5457
5458 /* {{{ */
PHP_METHOD(DatePeriod,__serialize)5459 PHP_METHOD(DatePeriod, __serialize)
5460 {
5461 zval *object = ZEND_THIS;
5462 php_period_obj *period_obj;
5463 HashTable *myht;
5464
5465 ZEND_PARSE_PARAMETERS_NONE();
5466
5467 period_obj = Z_PHPPERIOD_P(object);
5468 DATE_CHECK_INITIALIZED(period_obj->start, DatePeriod);
5469
5470 array_init(return_value);
5471 myht = Z_ARRVAL_P(return_value);
5472 date_period_object_to_hash(period_obj, myht);
5473
5474 add_common_properties(myht, &period_obj->std);
5475 }
5476 /* }}} */
5477
5478 /* {{{ date_period_is_internal_property
5479 * Common for date_period_read_property(), date_period_write_property(), and
5480 * restore_custom_dateperiod_properties functions
5481 */
date_period_is_internal_property(zend_string * name)5482 static bool date_period_is_internal_property(zend_string *name)
5483 {
5484 if (
5485 zend_string_equals_literal(name, "start") ||
5486 zend_string_equals_literal(name, "current") ||
5487 zend_string_equals_literal(name, "end") ||
5488 zend_string_equals_literal(name, "interval") ||
5489 zend_string_equals_literal(name, "recurrences") ||
5490 zend_string_equals_literal(name, "include_start_date") ||
5491 zend_string_equals_literal(name, "include_end_date")
5492 ) {
5493 return 1;
5494 }
5495 return 0;
5496 }
5497 /* }}} */
5498
restore_custom_dateperiod_properties(zval * object,HashTable * myht)5499 static void restore_custom_dateperiod_properties(zval *object, HashTable *myht)
5500 {
5501 zend_string *prop_name;
5502 zval *prop_val;
5503
5504 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(myht, prop_name, prop_val) {
5505 if (!prop_name || (Z_TYPE_P(prop_val) == IS_REFERENCE) || date_period_is_internal_property(prop_name)) {
5506 continue;
5507 }
5508 update_property(Z_OBJ_P(object), prop_name, prop_val);
5509 } ZEND_HASH_FOREACH_END();
5510 }
5511
5512 /* {{{ */
PHP_METHOD(DatePeriod,__unserialize)5513 PHP_METHOD(DatePeriod, __unserialize)
5514 {
5515 zval *object = ZEND_THIS;
5516 php_period_obj *period_obj;
5517 zval *array;
5518 HashTable *myht;
5519
5520 ZEND_PARSE_PARAMETERS_START(1, 1)
5521 Z_PARAM_ARRAY(array)
5522 ZEND_PARSE_PARAMETERS_END();
5523
5524 period_obj = Z_PHPPERIOD_P(object);
5525 myht = Z_ARRVAL_P(array);
5526
5527 if (!php_date_period_initialize_from_hash(period_obj, myht)) {
5528 zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
5529 RETURN_THROWS();
5530 }
5531 restore_custom_dateperiod_properties(object, myht);
5532 }
5533 /* }}} */
5534
5535 /* {{{ */
PHP_METHOD(DatePeriod,__wakeup)5536 PHP_METHOD(DatePeriod, __wakeup)
5537 {
5538 zval *object = ZEND_THIS;
5539 php_period_obj *period_obj;
5540 HashTable *myht;
5541
5542 ZEND_PARSE_PARAMETERS_NONE();
5543
5544 period_obj = Z_PHPPERIOD_P(object);
5545
5546 myht = Z_OBJPROP_P(object);
5547
5548 if (!php_date_period_initialize_from_hash(period_obj, myht)) {
5549 zend_throw_error(NULL, "Invalid serialization data for DatePeriod object");
5550 RETURN_THROWS();
5551 }
5552 }
5553 /* }}} */
5554
5555 /* {{{ date_period_read_property */
date_period_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)5556 static zval *date_period_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
5557 {
5558 if (type != BP_VAR_IS && type != BP_VAR_R) {
5559 if (date_period_is_internal_property(name)) {
5560 zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
5561 return &EG(uninitialized_zval);
5562 }
5563 }
5564
5565 return zend_std_read_property(object, name, type, cache_slot, rv);
5566 }
5567 /* }}} */
5568
date_period_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)5569 static zval *date_period_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
5570 {
5571 if (date_period_is_internal_property(name)) {
5572 zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
5573 return value;
5574 }
5575
5576 return zend_std_write_property(object, name, value, cache_slot);
5577 }
5578
date_period_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)5579 static zval *date_period_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
5580 {
5581 if (date_period_is_internal_property(name)) {
5582 zend_throw_error(NULL, "Cannot modify readonly property DatePeriod::$%s", ZSTR_VAL(name));
5583 return &EG(error_zval);
5584 }
5585
5586 return zend_std_get_property_ptr_ptr(object, name, type, cache_slot);
5587 }
5588