php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #53437
Patch date_patch_var4.patch revision 2013-03-14 15:21 UTC by ab@php.net
Patch date_patch_var3.patch revision 2013-03-13 08:53 UTC by ab@php.net
Patch glopes_date_5.3.patch revision 2013-03-06 18:50 UTC by ab@php.net
revision 2013-03-05 11:19 UTC by ab@php.net
Patch glopes_date_5.4.patch revision 2013-03-05 11:20 UTC by ab@php.net

Patch glopes_date_5.3.patch for Date/time related Bug #53437

Patch version 2013-03-06 18:50 UTC

Return to Bug #53437 | Download this patch
This patch is obsolete

Obsoleted by patches:

This patch renders other patches obsolete

Obsolete patches:

Patch Revisions:

Developer: ab@php.net

diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index 2e616b1..e9d9f17 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -23,6 +23,7 @@
 #include "php_main.h"
 #include "php_globals.h"
 #include "php_ini.h"
+#include "ext/standard/base64.h"
 #include "ext/standard/info.h"
 #include "ext/standard/php_versioning.h"
 #include "ext/standard/php_math.h"
@@ -472,6 +473,8 @@ const zend_function_entry date_funcs_interval[] = {
 
 const zend_function_entry date_funcs_period[] = {
 	PHP_ME(DatePeriod,                __construct,                 arginfo_date_period_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
+	PHP_ME(DatePeriod,                __wakeup,			           NULL, ZEND_ACC_PUBLIC)
+	PHP_ME(DatePeriod,                __set_state,                 NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 	PHP_FE_END
 };
 
@@ -565,9 +568,16 @@ static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC);
 static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC);
 static HashTable *date_object_get_properties(zval *object TSRMLS_DC);
 static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC);
+static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC);
 
 zval *date_interval_read_property(zval *object, zval *member, int type TSRMLS_DC);
 void date_interval_write_property(zval *object, zval *member, zval *value TSRMLS_DC);
+static zval *date_period_read_property(zval *object, zval *member, int type TSRMLS_DC);
+static zval **date_period_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC);
+static void date_period_write_property(zval *object, zval *member, zval *value TSRMLS_DC);
+
+static int php_date_interval_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
+static int php_date_interval_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
 
 /* {{{ Module struct */
 zend_module_entry date_module_entry = {
@@ -2000,6 +2010,8 @@ static void date_register_classes(TSRMLS_D)
 	INIT_CLASS_ENTRY(ce_interval, "DateInterval", date_funcs_interval);
 	ce_interval.create_object = date_object_new_interval;
 	date_ce_interval = zend_register_internal_class_ex(&ce_interval, NULL, NULL TSRMLS_CC);
+	date_ce_interval->serialize = php_date_interval_serialize;
+	date_ce_interval->unserialize = php_date_interval_unserialize;
 	memcpy(&date_object_handlers_interval, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 	date_object_handlers_interval.clone_obj = date_object_clone_interval;
 	date_object_handlers_interval.read_property = date_interval_read_property;
@@ -2015,6 +2027,10 @@ static void date_register_classes(TSRMLS_D)
 	zend_class_implements(date_ce_period TSRMLS_CC, 1, zend_ce_traversable);
 	memcpy(&date_object_handlers_period, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 	date_object_handlers_period.clone_obj = date_object_clone_period;
+	date_object_handlers_period.get_properties = date_object_get_properties_period;
+	date_object_handlers_period.read_property = date_period_read_property;
+	date_object_handlers_period.get_property_ptr_ptr = date_period_get_property_ptr_ptr;
+	date_object_handlers_period.write_property = date_period_write_property;
 
 #define REGISTER_PERIOD_CLASS_CONST_STRING(const_name, value) \
 	zend_declare_class_constant_long(date_ce_period, const_name, sizeof(const_name)-1, value TSRMLS_CC);
@@ -2097,40 +2113,29 @@ static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC)
 	return 1;
 }
 
-static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
+static void date_object_to_serializable_elements(timelib_time *time, HashTable *out_container)
 {
-	HashTable *props;
 	zval *zv;
-	php_date_obj     *dateobj;
-
-
-	dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
-
-	props = zend_std_get_properties(object TSRMLS_CC);
-
-	if (!dateobj->time || GC_G(gc_active)) {
-		return props;
-	}
 
 	/* first we add the date and time in ISO format */
 	MAKE_STD_ZVAL(zv);
-	ZVAL_STRING(zv, date_format("Y-m-d H:i:s", 12, dateobj->time, 1), 0);
-	zend_hash_update(props, "date", 5, &zv, sizeof(zval), NULL);
+	ZVAL_STRING(zv, date_format("Y-m-d H:i:s", 12, time, 1), 0);
+	zend_hash_update(out_container, "date", 5, &zv, sizeof(zval), NULL);
 
 	/* then we add the timezone name (or similar) */
-	if (dateobj->time->is_localtime) {
+	if (time->is_localtime) {
 		MAKE_STD_ZVAL(zv);
-		ZVAL_LONG(zv, dateobj->time->zone_type);
-		zend_hash_update(props, "timezone_type", 14, &zv, sizeof(zval), NULL);
+		ZVAL_LONG(zv, time->zone_type);
+		zend_hash_update(out_container, "timezone_type", 14, &zv, sizeof(zval), NULL);
 
 		MAKE_STD_ZVAL(zv);
-		switch (dateobj->time->zone_type) {
+		switch (time->zone_type) {
 			case TIMELIB_ZONETYPE_ID:
-				ZVAL_STRING(zv, dateobj->time->tz_info->name, 1);
+				ZVAL_STRING(zv, time->tz_info->name, 1);
 				break;
 			case TIMELIB_ZONETYPE_OFFSET: {
 				char *tmpstr = emalloc(sizeof("UTC+05:00"));
-				timelib_sll utc_offset = dateobj->time->z;
+				timelib_sll utc_offset = time->z;
 
 				snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d",
 					utc_offset > 0 ? '-' : '+',
@@ -2141,12 +2146,29 @@ static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
 				}
 				break;
 			case TIMELIB_ZONETYPE_ABBR:
-				ZVAL_STRING(zv, dateobj->time->tz_abbr, 1);
+				ZVAL_STRING(zv, time->tz_abbr, 1);
 				break;
 		}
-		zend_hash_update(props, "timezone", 9, &zv, sizeof(zval), NULL);
+		zend_hash_update(out_container, "timezone", 9, &zv, sizeof(zval), NULL);
 	}
+}
+
+static HashTable *date_object_get_properties(zval *object TSRMLS_DC)
+{
+	HashTable *props;
+	php_date_obj     *dateobj;
 
+
+	dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+
+	props = zend_std_get_properties(object TSRMLS_CC);
+
+	if (!dateobj->time || GC_G(gc_active)) {
+		return props;
+	}
+
+	date_object_to_serializable_elements(dateobj->time, props);
+	
 	return props;
 }
 
@@ -2246,11 +2268,10 @@ static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC)
 
 static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC)
 {
-	HashTable *props;
-	zval *zv;
-	php_interval_obj     *intervalobj;
-
-
+	HashTable		 *props;
+	php_interval_obj *intervalobj;
+	zval			 *zv;
+	
 	intervalobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
 
 	props = zend_std_get_properties(object TSRMLS_CC);
@@ -2262,7 +2283,7 @@ static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC)
 #define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \
 	MAKE_STD_ZVAL(zv); \
 	ZVAL_LONG(zv, intervalobj->diff->f); \
-	zend_hash_update(props, n, strlen(n) + 1, &zv, sizeof(zval), NULL);
+	zend_hash_update(props, n, sizeof(n), &zv, sizeof(zval), NULL);
 
 	PHP_DATE_INTERVAL_ADD_PROPERTY("y", y);
 	PHP_DATE_INTERVAL_ADD_PROPERTY("m", m);
@@ -2279,6 +2300,8 @@ static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC)
 		zend_hash_update(props, "days", 5, &zv, sizeof(zval), NULL);
 	}
 
+#undef PHP_DATE_INTERVAL_ADD_PROPERTY
+
 	return props;
 }
 
@@ -2320,6 +2343,78 @@ static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC)
 	return new_ov;
 }
 
+static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC)
+{
+	HashTable		*props;
+	zval			*zv;
+	php_period_obj	*period_obj;
+
+
+	period_obj = zend_object_store_get_object(object TSRMLS_CC);
+
+	props = zend_std_get_properties(object TSRMLS_CC);
+
+	if (!period_obj->start || GC_G(gc_active)) {
+		return props;
+	}
+
+	MAKE_STD_ZVAL(zv);
+	if (period_obj->start) {
+		php_date_obj *date_obj;
+		object_init_ex(zv, date_ce_date);
+		date_obj = zend_object_store_get_object(zv TSRMLS_CC);
+		date_obj->time = timelib_time_clone(period_obj->start);
+	} else {
+		ZVAL_NULL(zv);
+	}
+	zend_hash_update(props, "start", sizeof("start"), &zv, sizeof(zv), NULL);
+
+	MAKE_STD_ZVAL(zv);
+	if (period_obj->current) {
+		php_date_obj *date_obj;
+		object_init_ex(zv, date_ce_date);
+		date_obj = zend_object_store_get_object(zv TSRMLS_CC);
+		date_obj->time = timelib_time_clone(period_obj->current);
+	} else {
+		ZVAL_NULL(zv);
+	}
+	zend_hash_update(props, "current", sizeof("current"), &zv, sizeof(zv), NULL);
+
+	MAKE_STD_ZVAL(zv);
+	if (period_obj->end) {
+		php_date_obj *date_obj;
+		object_init_ex(zv, date_ce_date);
+		date_obj = zend_object_store_get_object(zv TSRMLS_CC);
+		date_obj->time = timelib_time_clone(period_obj->end);
+	} else {
+		ZVAL_NULL(zv);
+	}
+	zend_hash_update(props, "end", sizeof("end"), &zv, sizeof(zv), NULL);
+
+	MAKE_STD_ZVAL(zv);
+	if (period_obj->interval) {
+		php_interval_obj *interval_obj;
+		object_init_ex(zv, date_ce_interval);
+		interval_obj = zend_object_store_get_object(zv TSRMLS_CC);
+		interval_obj->diff = timelib_rel_time_clone(period_obj->interval);
+		interval_obj->initialized = 1;
+	} else {
+		ZVAL_NULL(zv);
+	}
+	zend_hash_update(props, "interval", sizeof("interval"), &zv, sizeof(zv), NULL);
+	
+	/* converted to larger type (int->long); must check when unserializing */
+	MAKE_STD_ZVAL(zv);
+	ZVAL_LONG(zv, (long) period_obj->recurrences);
+	zend_hash_update(props, "recurrences", sizeof("recurrences"), &zv, sizeof(zv), NULL);
+
+	MAKE_STD_ZVAL(zv);
+	ZVAL_BOOL(zv, period_obj->include_start_date);
+	zend_hash_update(props, "include_start_date", sizeof("include_start_date"), &zv, sizeof(zv), NULL);
+
+	return props;
+}
+
 static void date_object_free_storage_date(void *object TSRMLS_DC)
 {
 	php_date_obj *intern = (php_date_obj *)object;
@@ -2534,7 +2629,7 @@ PHP_METHOD(DateTime, __construct)
 }
 /* }}} */
 
-static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dateobj, HashTable *myht TSRMLS_DC)
+static int php_date_initialize_from_hash(php_date_obj *dateobj, HashTable *myht TSRMLS_DC)
 {
 	zval            **z_date = NULL;
 	zval            **z_timezone = NULL;
@@ -2555,7 +2650,7 @@ static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dat
 					case TIMELIB_ZONETYPE_ABBR: {
 						char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2);
 						snprintf(tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2, "%s %s", Z_STRVAL_PP(z_date), Z_STRVAL_PP(z_timezone));
-						php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC);
+						php_date_initialize(dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC);
 						efree(tmp);
 						return 1;
 					}
@@ -2571,7 +2666,7 @@ static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dat
 						tzobj->tzi.tz = tzi;
 						tzobj->initialized = 1;
 
-						php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC);
+						php_date_initialize(dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC);
 						zval_ptr_dtor(&tmp_obj);
 						return 1;
 				}
@@ -2597,7 +2692,7 @@ PHP_METHOD(DateTime, __set_state)
 
 	php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
 	dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC);
-	php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC);
+	php_date_initialize_from_hash(dateobj, myht TSRMLS_CC);
 }
 /* }}} */
 
@@ -2613,7 +2708,7 @@ PHP_METHOD(DateTime, __wakeup)
 
 	myht = Z_OBJPROP_P(object);
 
-	php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC);
+	php_date_initialize_from_hash(dateobj, myht TSRMLS_CC);
 }
 /* }}} */
 
@@ -3691,20 +3786,6 @@ PHP_METHOD(DateInterval, __set_state)
 }
 /* }}} */
 
-/* {{{ proto DateInterval::__wakeup()
-*/
-PHP_METHOD(DateInterval, __wakeup)
-{
-	zval             *object = getThis();
-	php_interval_obj *intobj;
-	HashTable        *myht;
-
-	intobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC);
-
-	myht = Z_OBJPROP_P(object);
-
-	php_date_interval_initialize_from_hash(&return_value, &intobj, myht TSRMLS_CC);
-}
 /* }}} */
 /* {{{ proto DateInterval date_interval_create_from_date_string(string time)
    Uses the normal date parsers and sets up a DateInterval from the relative parts of the parsed string
@@ -3732,6 +3813,187 @@ PHP_FUNCTION(date_interval_create_from_date_string)
 }
 /* }}} */
 
+#define PHP_DATE_INTERVAL_UNSER_RAW_SIZE 92 /* 6 * 8 + 4 * 4 + 8 + (4 + 8) + 2 * 4; not null-terminated */
+
+static int php_date_interval_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
+{
+	php_interval_obj *interval_obj;
+	timelib_rel_time *or_trl;
+	unsigned char	 tmp[PHP_DATE_INTERVAL_UNSER_RAW_SIZE], 
+					 *b64_res;
+	int				 cursor = 0,
+					 b64_len = 0;
+
+#define PHP_SER_WRITE_I32(v) \
+	{ \
+		uint32_t t = htonl((uint32_t) (v)); \
+		memcpy(&tmp[cursor], (const void *) &t, sizeof(t)); \
+		cursor += sizeof(t); \
+	}
+
+#define PHP_SER_WRITE_I64(v) \
+	PHP_SER_WRITE_I32(((uint64_t) (v)) >> 32) \
+	PHP_SER_WRITE_I32(((uint64_t) (v)) & 0xFFFFFFFF)
+
+
+	interval_obj = zend_object_store_get_object(object TSRMLS_CC);
+	or_trl = interval_obj->diff;
+
+	PHP_SER_WRITE_I64(or_trl->y);
+	PHP_SER_WRITE_I64(or_trl->m);
+	PHP_SER_WRITE_I64(or_trl->d);
+	PHP_SER_WRITE_I64(or_trl->h);
+	PHP_SER_WRITE_I64(or_trl->i);
+	PHP_SER_WRITE_I64(or_trl->s);
+	PHP_SER_WRITE_I32(or_trl->weekday);
+	PHP_SER_WRITE_I32(or_trl->weekday_behavior);
+	PHP_SER_WRITE_I32(or_trl->first_last_day_of);
+	PHP_SER_WRITE_I32(or_trl->invert);
+	PHP_SER_WRITE_I64(or_trl->days);
+	PHP_SER_WRITE_I32(or_trl->special.type);
+	PHP_SER_WRITE_I64(or_trl->special.amount);
+	PHP_SER_WRITE_I32(or_trl->have_weekday_relative);
+	PHP_SER_WRITE_I32(or_trl->have_special_relative);
+	
+#undef PHP_SER_WRITE_I32
+#undef PHP_SER_WRITE_I64
+
+	assert(cursor == PHP_DATE_INTERVAL_UNSER_RAW_SIZE);
+
+	/* base64 to make serialization data binary safe */
+	b64_res = php_base64_encode(tmp, sizeof(tmp), &b64_len);
+
+	if (b64_res == NULL) {
+		return FAILURE;
+	}
+
+	*buffer = b64_res;
+	*buf_len = b64_len;
+
+	return SUCCESS;
+}
+
+static int php_date_interval_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
+{
+	php_interval_obj *interval_obj;
+	timelib_rel_time *trl;
+	unsigned char	 *b64_unenc;
+	int				 b64_unenc_size,
+					 cursor = 0;
+
+	/* should be 124, but we prefer the stricter check on the unencoded size instead */
+	if (ce != date_ce_interval || buf_len > 150) {
+		return FAILURE;
+	}
+
+	b64_unenc = php_base64_decode_ex(buf, (int) buf_len, &b64_unenc_size, 1);
+	if (b64_unenc == NULL) {
+		return FAILURE;
+	}
+	if (b64_unenc_size != PHP_DATE_INTERVAL_UNSER_RAW_SIZE) {
+		efree(b64_unenc);
+		return FAILURE;
+	}
+
+	if (*object == NULL) {
+		MAKE_STD_ZVAL(*object);
+	}
+	object_init_ex(*object, ce);
+
+	interval_obj = zend_object_store_get_object(*object TSRMLS_CC);
+	trl = timelib_rel_time_ctor();
+	interval_obj->diff = trl;
+
+/* copy to signed 32_t, preserving bit pattern (casting the result of ntohl and
+ * assigning to st would probably suffice, but it's technically implementation
+ * defined behavior) and then assign to the field. This is done to force sign-
+ * extension if an int has 64 bits and the field is signed. If the field is
+ * unsigned, it would have the side-effect of corrupting values >= 2^31, as in
+ * (uint32_t)0xFFFFFFFE -> (int32_t)-2 -> (uint64_t)FFFFFFFFFFFFFFFE, which is
+ * why it isn't used for those fields.  */
+#define PHP_SER_READ_I32_SIGNED(v) \
+	{ \
+		int32_t st; \
+		*((uint32_t*) &st) = ntohl(*((uint32_t*) &b64_unenc[cursor])); \
+		(v) = st; \
+		cursor += sizeof(uint32_t); \
+	}
+
+#define PHP_SER_READ_I32_UNSIGNED(v) \
+	(v) = ntohl(*((uint32_t*) &b64_unenc[cursor])); \
+	cursor += sizeof(uint32_t); \
+
+#define PHP_SER_READ_I64(v) \
+	*((uint64_t*) &(v)) = (((uint64_t) ntohl(*((uint32_t*) &b64_unenc[cursor]))) << 32) | \
+		((uint64_t) ntohl(*((uint32_t*) &b64_unenc[cursor + sizeof(uint32_t)]))); \
+	cursor += sizeof(uint64_t);
+
+	PHP_SER_READ_I64(trl->y);
+	PHP_SER_READ_I64(trl->m);
+	PHP_SER_READ_I64(trl->d);
+	PHP_SER_READ_I64(trl->h);
+	PHP_SER_READ_I64(trl->i);
+	PHP_SER_READ_I64(trl->s);
+	PHP_SER_READ_I32_SIGNED(trl->weekday);
+	PHP_SER_READ_I32_SIGNED(trl->weekday_behavior);
+	PHP_SER_READ_I32_SIGNED(trl->first_last_day_of);
+	PHP_SER_READ_I32_SIGNED(trl->invert);
+	PHP_SER_READ_I64(trl->days);
+	PHP_SER_READ_I32_UNSIGNED(trl->special.type);
+	PHP_SER_READ_I64(trl->special.amount);
+	PHP_SER_READ_I32_UNSIGNED(trl->have_weekday_relative);
+	PHP_SER_READ_I32_UNSIGNED(trl->have_special_relative);
+
+#undef PHP_SER_READ_I32
+#undef PHP_SER_READ_I64
+
+	efree(b64_unenc);
+
+	assert(cursor == PHP_DATE_INTERVAL_UNSER_RAW_SIZE);
+
+	/* in case they're somewhere tested with if (rtime->have_weekday_relative == 1)... */
+	trl->invert = !!trl->invert;
+	trl->have_weekday_relative = !!trl->have_weekday_relative;
+	trl->have_special_relative = !!trl->have_special_relative;
+
+	interval_obj->initialized = 1;
+
+	return SUCCESS;
+}
+
+/* {{{ proto DateInterval::__wakeup()
+*/
+PHP_METHOD(DateInterval, __wakeup)
+{
+	zval             *object = getThis();
+	php_interval_obj *interval_obj;
+	timelib_rel_time *rtime;
+
+	interval_obj = zend_object_store_get_object(object TSRMLS_CC);
+	rtime = interval_obj->diff;
+	
+	/* validate state of DateInterval object */
+	if (rtime->weekday < -7 || rtime->weekday > 6) {
+		php_error(E_ERROR, "Invalid serialization data for DateInterval object (weekday)");
+	}
+	if (rtime->weekday_behavior < 0 || rtime->weekday_behavior > 2) {
+		php_error(E_ERROR, "Invalid serialization data for DateInterval object (weekday_behavior)");
+	}
+	if (rtime->have_special_relative && (
+			rtime->special.type != TIMELIB_SPECIAL_WEEKDAY &&
+			rtime->special.type != TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH &&
+			rtime->special.type != TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH)) {
+		php_error(E_ERROR, "Invalid serialization data for DateInterval object (special.type)");
+	}
+	if (rtime->special.amount > 12 || rtime->special.amount < -12) {
+		php_error(E_ERROR, "Invalid serialization data for DateInterval object (special.amount)");
+	}
+	if (rtime->first_last_day_of < 0 || rtime->first_last_day_of > 2) {
+		php_error(E_ERROR, "Invalid serialization data for DateInterval object (first_last_day_of)");
+	}
+}
+/* }}} */
+
 /* {{{ date_interval_format -  */
 static char *date_interval_format(char *format, int format_len, timelib_rel_time *t)
 {
@@ -3838,6 +4100,43 @@ static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_
 	return retval;
 }
 
+/* {{{ date_period_read_property */
+static zval *date_period_read_property(zval *object, zval *member, int type TSRMLS_DC)
+{
+	zval *zv;
+	if (type != BP_VAR_IS && type != BP_VAR_R) {
+		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported");
+	}
+
+	Z_OBJPROP_P(object); /* build properties hash table */
+
+	zv = std_object_handlers.read_property(object, member, type TSRMLS_CC);
+	if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) {
+		/* defensive copy */
+		zend_object_value zov = Z_OBJ_HANDLER_P(zv, clone_obj)(zv TSRMLS_CC);
+		MAKE_STD_ZVAL(zv);
+		Z_TYPE_P(zv) = IS_OBJECT;
+		Z_OBJVAL_P(zv) = zov;
+	}
+
+	return zv;
+}
+/* }}} */
+
+/* {{{ date_period_get_property_ptr_ptr */
+static zval **date_period_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC)
+{
+	return NULL; /* force fallback to read_property */
+}
+/* }}} */
+
+/* {{{ date_period_write_property */
+static void date_period_write_property(zval *object, zval *member, zval *value TSRMLS_DC)
+{
+	php_error_docref(NULL TSRMLS_CC, E_ERROR, "Writing to DatePeriod properties is unsupported");
+}
+/* }}} */
+
 /* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end)
    Creates new DatePeriod object.
 */
@@ -3924,6 +4223,119 @@ PHP_METHOD(DatePeriod, __construct)
 }
 /* }}} */
 
+static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht TSRMLS_DC)
+{
+	zval **ht_entry;
+
+	/* this function does no rollback on error */
+
+	if (zend_hash_find(myht, "start", sizeof("start"), (void**) &ht_entry) == SUCCESS) {
+		if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
+			php_date_obj *date_obj;
+			date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
+			period_obj->start = timelib_time_clone(date_obj->time);
+		} else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+
+	if (zend_hash_find(myht, "end", sizeof("end"), (void**) &ht_entry) == SUCCESS) {
+		if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
+			php_date_obj *date_obj;
+			date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
+			period_obj->end = timelib_time_clone(date_obj->time);
+		} else if (Z_TYPE_PP(ht_entry) != IS_NULL) {
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+
+	if (zend_hash_find(myht, "current", sizeof("current"), (void**) &ht_entry) == SUCCESS) {
+		if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) {
+			php_date_obj *date_obj;
+			date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
+			period_obj->current = timelib_time_clone(date_obj->time);
+		} else if (Z_TYPE_PP(ht_entry) != IS_NULL)  {
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+
+	if (zend_hash_find(myht, "interval", sizeof("interval"), (void**) &ht_entry) == SUCCESS) {
+		if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_interval) {
+			php_interval_obj *interval_obj;
+			interval_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC);
+			period_obj->interval = timelib_rel_time_clone(interval_obj->diff);
+		} else { /* interval is required */
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+
+	if (zend_hash_find(myht, "recurrences", sizeof("recurrences"), (void**) &ht_entry) == SUCCESS &&
+			Z_TYPE_PP(ht_entry) == IS_LONG && Z_LVAL_PP(ht_entry) >= 0 && Z_LVAL_PP(ht_entry) <= INT_MAX) {
+		period_obj->recurrences = Z_LVAL_PP(ht_entry);
+	} else {
+		return 0;
+	}
+
+	if (zend_hash_find(myht, "include_start_date", sizeof("include_start_date"), (void**) &ht_entry) == SUCCESS &&
+			Z_TYPE_PP(ht_entry) == IS_BOOL) {
+		period_obj->include_start_date = Z_BVAL_PP(ht_entry);
+	} else {
+		return 0;
+	}
+
+	period_obj->initialized = 1;
+	
+	return 1;
+}
+
+/* {{{ proto DatePeriod::__set_state()
+*/
+PHP_METHOD(DatePeriod, __set_state)
+{
+	php_period_obj   *period_obj;
+	zval             *array;
+	HashTable        *myht;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
+		RETURN_FALSE;
+	}
+
+	myht = Z_ARRVAL_P(array);
+	
+	object_init_ex(return_value, date_ce_period);
+	period_obj = zend_object_store_get_object(return_value TSRMLS_CC);
+	if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
+		php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
+	}
+}
+/* }}} */
+
+/* {{{ proto DatePeriod::__wakeup()
+*/
+PHP_METHOD(DatePeriod, __wakeup)
+{
+	zval             *object = getThis();
+	php_period_obj   *period_obj;
+	HashTable        *myht;
+
+	period_obj = zend_object_store_get_object(object TSRMLS_CC);
+
+	myht = Z_OBJPROP_P(object);
+
+	if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) {
+		php_error(E_ERROR, "Invalid serialization data for DatePeriod object");
+	}
+}
+/* }}} */
+
 static int check_id_allowed(char *id, long what)
 {
 	if (what & PHP_DATE_TIMEZONE_GROUP_AFRICA     && strncasecmp(id, "Africa/",      7) == 0) return 1;
diff --git a/ext/date/php_date.h b/ext/date/php_date.h
index a86d6c7..16837df 100644
--- a/ext/date/php_date.h
+++ b/ext/date/php_date.h
@@ -88,6 +88,8 @@ PHP_FUNCTION(date_interval_format);
 PHP_FUNCTION(date_interval_create_from_date_string);
 
 PHP_METHOD(DatePeriod, __construct);
+PHP_METHOD(DatePeriod, __wakeup);
+PHP_METHOD(DatePeriod, __set_state);
 
 /* Options and Configuration */
 PHP_FUNCTION(date_default_timezone_set);
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Fri Apr 18 00:01:21 2014 UTC