diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 7d3d1d739e113..7114b109a2734 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -26,6 +26,7 @@ #include "ext/standard/info.h" #include "ext/standard/php_versioning.h" #include "ext/standard/php_math.h" +#include "ext/standard/php_var.h" #include "php_date.h" #include "zend_interfaces.h" #include "lib/timelib.h" @@ -450,13 +451,11 @@ static const zend_function_entry date_funcs_interface[] = { PHP_ABSTRACT_ME(DateTimeInterface, getOffset, arginfo_date_method_offset_get) PHP_ABSTRACT_ME(DateTimeInterface, getTimestamp, arginfo_date_method_timestamp_get) PHP_ABSTRACT_ME(DateTimeInterface, diff, arginfo_date_method_diff) - PHP_ABSTRACT_ME(DateTimeInterface, __wakeup, NULL) PHP_FE_END }; const zend_function_entry date_funcs_date[] = { PHP_ME(DateTime, __construct, arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) - PHP_ME(DateTime, __wakeup, NULL, ZEND_ACC_PUBLIC) PHP_ME(DateTime, __set_state, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(createFromFormat, date_create_from_format, arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(getLastErrors, date_get_last_errors, arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -478,7 +477,6 @@ const zend_function_entry date_funcs_date[] = { const zend_function_entry date_funcs_immutable[] = { PHP_ME(DateTimeImmutable, __construct, arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) - PHP_ME(DateTime, __wakeup, NULL, ZEND_ACC_PUBLIC) PHP_ME(DateTimeImmutable, __set_state, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(createFromFormat, date_create_immutable_from_format, arginfo_date_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(getLastErrors, date_get_last_errors, arginfo_date_get_last_errors, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -623,9 +621,13 @@ static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC); static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC); static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC); +static int date_object_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); +static int date_object_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); + +static void date_object_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC); static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC); static HashTable *date_object_get_gc(zval *object, zval ***table, int *n TSRMLS_DC); -static HashTable *date_object_get_properties(zval *object TSRMLS_DC); +static HashTable *date_object_get_debug_info(zval *object, int *is_temp TSRMLS_DC); static HashTable *date_object_get_gc_interval(zval *object, zval ***table, int *n TSRMLS_DC); static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC); static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC); @@ -1989,11 +1991,14 @@ static void date_register_classes(TSRMLS_D) INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date); ce_date.create_object = date_object_new_date; + ce_date.serialize = date_object_serialize; + ce_date.unserialize = date_object_unserialize; date_ce_date = zend_register_internal_class_ex(&ce_date, NULL, NULL TSRMLS_CC); memcpy(&date_object_handlers_date, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + date_object_handlers_date.write_property = date_object_write_property; date_object_handlers_date.clone_obj = date_object_clone_date; date_object_handlers_date.compare_objects = date_object_compare_date; - date_object_handlers_date.get_properties = date_object_get_properties; + date_object_handlers_date.get_debug_info = date_object_get_debug_info; date_object_handlers_date.get_gc = date_object_get_gc; zend_class_implements(date_ce_date TSRMLS_CC, 1, date_ce_interface); @@ -2014,11 +2019,13 @@ static void date_register_classes(TSRMLS_D) INIT_CLASS_ENTRY(ce_immutable, "DateTimeImmutable", date_funcs_immutable); ce_immutable.create_object = date_object_new_date; + ce_immutable.serialize = date_object_serialize; + ce_immutable.unserialize = date_object_unserialize; date_ce_immutable = zend_register_internal_class_ex(&ce_immutable, NULL, NULL TSRMLS_CC); memcpy(&date_object_handlers_immutable, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); date_object_handlers_immutable.clone_obj = date_object_clone_date; date_object_handlers_immutable.compare_objects = date_object_compare_date; - date_object_handlers_immutable.get_properties = date_object_get_properties; + date_object_handlers_immutable.get_debug_info = date_object_get_debug_info; zend_class_implements(date_ce_immutable TSRMLS_CC, 1, date_ce_interface); INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", date_funcs_timezone); @@ -2140,6 +2147,21 @@ static zval* date_clone_immutable(zval *object TSRMLS_DC) return new_object; } +static inline int date_object_is_internal_property_key(char *key) +{ + return key && (!strcmp(key, "date") || !strcmp(key, "timezone") ||!strcmp(key, "timezone_type")); +} + +static void date_object_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) +{ + if ( Z_TYPE_P(member) == IS_STRING && date_object_is_internal_property_key(Z_STRVAL_P(member))) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Redefining internal property '%s' is not allowed", Z_STRVAL_P(member)); + return; + } + zend_std_write_property(object, member, value, key TSRMLS_CC); +} + + static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC) { php_date_obj *o1 = zend_object_store_get_object(d1 TSRMLS_CC); @@ -2174,31 +2196,32 @@ static HashTable *date_object_get_gc_timezone(zval *object, zval ***table, int * return zend_std_get_properties(object TSRMLS_CC); } -static HashTable *date_object_get_properties(zval *object TSRMLS_DC) +static HashTable *date_object_get_debug_info(zval *object, int *is_temp TSRMLS_DC) { - HashTable *props; - zval *zv; - php_date_obj *dateobj; - + HashTable *debug_props, *object_props; + zval *zv, *tmp; + php_date_obj *dateobj; dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - props = zend_std_get_properties(object TSRMLS_CC); + ALLOC_HASHTABLE(debug_props); + zend_hash_init(debug_props, 3, NULL, ZVAL_PTR_DTOR, 0); + *is_temp = 1; if (!dateobj->time || GC_G(gc_active)) { - return props; + return debug_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(zv), NULL); + ZVAL_STRING(zv, date_format("Y-m-d H:i:s", sizeof("Y-m-d H:i:s"), dateobj->time, 1), 0); + zend_hash_update(debug_props, "date", sizeof("date"), &zv, sizeof(zv), NULL); /* then we add the timezone name (or similar) */ if (dateobj->time->is_localtime) { MAKE_STD_ZVAL(zv); ZVAL_LONG(zv, dateobj->time->zone_type); - zend_hash_update(props, "timezone_type", 14, &zv, sizeof(zv), NULL); + zend_hash_update(debug_props, "timezone_type", sizeof("timezone_type"), &zv, sizeof(zv), NULL); MAKE_STD_ZVAL(zv); switch (dateobj->time->zone_type) { @@ -2221,10 +2244,163 @@ static HashTable *date_object_get_properties(zval *object TSRMLS_DC) ZVAL_STRING(zv, dateobj->time->tz_abbr, 1); break; } - zend_hash_update(props, "timezone", 9, &zv, sizeof(zv), NULL); + zend_hash_update(debug_props, "timezone", sizeof("timezone"), &zv, sizeof(zv), NULL); } - return props; + /* merge debug props with object props */ + object_props = zend_std_get_properties(object TSRMLS_CC); + zend_hash_merge(debug_props, object_props, (void (*)(void *pData)) zval_add_ref, (void *) &tmp, sizeof(zval *), 0); + + return debug_props; +} + +static int date_object_serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) +{ + HashTable *object_props; + int object_props_num; + char *format; + php_date_obj *dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); + smart_str buf = {0}; + + object_props = zend_std_get_properties(object TSRMLS_CC); + object_props_num = zend_hash_num_elements(object_props); + + if (!dateobj->time) { + php_var_serialize_object_start(&buf, object, object_props_num TSRMLS_CC); + goto date_serialize_end; + } + + php_var_serialize_object_start(&buf, object, (object_props_num + (dateobj->time->is_localtime ? 3 : 1)) TSRMLS_CC); + + format = date_format("Y-m-d H:i:s", sizeof("Y-m-d H:i:s"), dateobj->time, 1); + php_var_serialize_property_string(&buf, "date", format); + efree(format); + + if (dateobj->time->is_localtime) { + php_var_serialize_property_long(&buf, "timezone_type", dateobj->time->zone_type); + switch (dateobj->time->zone_type) { + case TIMELIB_ZONETYPE_ID: + php_var_serialize_property_string(&buf, "timezone", dateobj->time->tz_info->name); + break; + case TIMELIB_ZONETYPE_OFFSET: { + char *tmpstr = emalloc(sizeof("UTC+05:00")); + timelib_sll utc_offset = dateobj->time->z; + + snprintf(tmpstr, sizeof("+05:00"), "%c%02d:%02d", + utc_offset > 0 ? '-' : '+', + abs(utc_offset / 60), + abs((utc_offset % 60))); + + php_var_serialize_property_string(&buf, "timezone", tmpstr); + efree(tmpstr); + } + break; + case TIMELIB_ZONETYPE_ABBR: + php_var_serialize_property_string(&buf, "timezone", dateobj->time->tz_abbr); + break; + } + } + +date_serialize_end: + if (object_props_num > 0) { + php_var_serialize_properties(&buf, object_props, data TSRMLS_CC); + } + php_var_serialize_object_end(&buf); + smart_str_0(&buf); + *buffer = (unsigned char *) buf.c; + *buf_len = buf.len; + + return PHP_SERIALIZE_OBJECT; +} + +static int date_object_initialize_from_hash(php_date_obj *dateobj, HashTable *myht TSRMLS_DC) +{ + zval **z_date = NULL; + zval **z_timezone = NULL; + zval **z_timezone_type = NULL; + zval *tmp_obj = NULL; + timelib_tzinfo *tzi; + php_timezone_obj *tzobj; + int ret = 0; + + if (zend_hash_find(myht, "date", 5, (void**) &z_date) == SUCCESS) { + convert_to_string(*z_date); + if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) { + convert_to_long(*z_timezone_type); + if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) { + convert_to_string(*z_timezone); + + switch (Z_LVAL_PP(z_timezone_type)) { + case TIMELIB_ZONETYPE_OFFSET: + 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)); + ret = php_date_initialize(dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC); + efree(tmp); + } + + case TIMELIB_ZONETYPE_ID: { + convert_to_string(*z_timezone); + + tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC); + + ALLOC_INIT_ZVAL(tmp_obj); + tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, tmp_obj TSRMLS_CC) TSRMLS_CC); + tzobj->type = TIMELIB_ZONETYPE_ID; + tzobj->tzi.tz = tzi; + tzobj->initialized = 1; + + ret = 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); + } + } + } + } + } + if (ret && zend_hash_num_elements(myht) > 3) { + char *key = NULL; + uint key_len; + ulong index; + zval *value; + zval **z_data; + + if (!dateobj->std.properties) { + rebuild_object_properties(&dateobj->std); + } + for (zend_hash_internal_pointer_reset(myht); zend_hash_get_current_data(myht, (void **) &z_data) == SUCCESS; zend_hash_move_forward(myht)) { + zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, NULL); + if (!date_object_is_internal_property_key(key)) { + MAKE_STD_ZVAL(value); + ZVAL_ZVAL(value, *z_data, 1, 0); + zend_hash_update(dateobj->std.properties, key, key_len, (void *) &value, sizeof(zval *), NULL); + } + } + } + return ret; +} + +static int date_object_unserialize(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) +{ + HashTable *myht; + php_date_obj *dateobj; + int retval; + + ALLOC_HASHTABLE(myht); + zend_hash_init(myht, 3, NULL, ZVAL_PTR_DTOR, 0); + + if (php_var_unserialize_properties(myht, &buf, &buf_len, data TSRMLS_CC)) { + dateobj = (php_date_obj *) zend_object_store_get_object(*object TSRMLS_CC); + if (!date_object_initialize_from_hash(dateobj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DateTime object"); + } + retval = (int) buf_len; + } else { + retval = -1; + } + + zend_hash_destroy(myht); + FREE_HASHTABLE(myht); + return retval; } static inline zend_object_value date_object_new_timezone_ex(zend_class_entry *class_type, php_timezone_obj **ptr TSRMLS_DC) @@ -2725,56 +2901,6 @@ PHP_METHOD(DateTimeImmutable, __construct) } /* }}} */ -static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dateobj, HashTable *myht TSRMLS_DC) -{ - zval **z_date = NULL; - zval **z_timezone = NULL; - zval **z_timezone_type = NULL; - zval *tmp_obj = NULL; - timelib_tzinfo *tzi; - php_timezone_obj *tzobj; - - if (zend_hash_find(myht, "date", 5, (void**) &z_date) == SUCCESS) { - convert_to_string(*z_date); - if (zend_hash_find(myht, "timezone_type", 14, (void**) &z_timezone_type) == SUCCESS) { - convert_to_long(*z_timezone_type); - if (zend_hash_find(myht, "timezone", 9, (void**) &z_timezone) == SUCCESS) { - convert_to_string(*z_timezone); - - switch (Z_LVAL_PP(z_timezone_type)) { - case TIMELIB_ZONETYPE_OFFSET: - case TIMELIB_ZONETYPE_ABBR: { - char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2); - int ret; - 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)); - ret = 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 == ret; - } - - case TIMELIB_ZONETYPE_ID: { - int ret; - convert_to_string(*z_timezone); - - tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC); - - ALLOC_INIT_ZVAL(tmp_obj); - tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, tmp_obj TSRMLS_CC) TSRMLS_CC); - tzobj->type = TIMELIB_ZONETYPE_ID; - tzobj->tzi.tz = tzi; - tzobj->initialized = 1; - - ret = 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 == ret; - } - } - } - } - } - return 0; -} - /* {{{ proto DateTime::__set_state() */ PHP_METHOD(DateTime, __set_state) @@ -2791,7 +2917,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); - if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { + if (!date_object_initialize_from_hash(dateobj, myht TSRMLS_CC)) { php_error(E_ERROR, "Invalid serialization data for DateTime object"); } } @@ -2813,30 +2939,12 @@ PHP_METHOD(DateTimeImmutable, __set_state) php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC); dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC); - if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { + if (!date_object_initialize_from_hash(dateobj, myht TSRMLS_CC)) { php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object"); } } /* }}} */ -/* {{{ proto DateTime::__wakeup() -*/ -PHP_METHOD(DateTime, __wakeup) -{ - zval *object = getThis(); - php_date_obj *dateobj; - HashTable *myht; - - dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - - myht = Z_OBJPROP_P(object); - - if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { - php_error(E_ERROR, "Invalid serialization data for DateTime object"); - } -} -/* }}} */ - /* Helper function used to add an associative array of warnings and errors to a zval */ static void zval_from_error_container(zval *z, timelib_error_container *error) { diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 536629a25fa64..1c3accaaff279 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -48,7 +48,6 @@ PHP_FUNCTION(getdate); /* Advanced Interface */ PHP_METHOD(DateTime, __construct); -PHP_METHOD(DateTime, __wakeup); PHP_METHOD(DateTime, __set_state); PHP_FUNCTION(date_create); PHP_FUNCTION(date_create_immutable); diff --git a/ext/date/tests/DateTime_clone_basic2.phpt b/ext/date/tests/DateTime_clone_basic2.phpt index db7ba2271939b..c12f3e4a3cf9d 100644 --- a/ext/date/tests/DateTime_clone_basic2.phpt +++ b/ext/date/tests/DateTime_clone_basic2.phpt @@ -32,30 +32,36 @@ var_dump($d2_clone); --EXPECTF-- *** Testing clone on objects whoose class derived from DateTime class *** object(DateTimeExt1)#%d (5) { + ["date"]=> + string(19) "2009-02-03 12:34:41" + ["timezone_type"]=> + int(2) + ["timezone"]=> + string(3) "GMT" ["property1"]=> int(99) ["property2"]=> string(5) "Hello" +} +object(DateTimeExt1)#%d (5) { ["date"]=> string(19) "2009-02-03 12:34:41" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "GMT" -} -object(DateTimeExt1)#%d (5) { ["property1"]=> int(99) ["property2"]=> string(5) "Hello" +} +object(DateTimeExt2)#%d (7) { ["date"]=> string(19) "2009-02-03 12:34:41" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "GMT" -} -object(DateTimeExt2)#%d (7) { ["property3"]=> bool(true) ["property4"]=> @@ -64,14 +70,14 @@ object(DateTimeExt2)#%d (7) { int(99) ["property2"]=> string(5) "Hello" +} +object(DateTimeExt2)#%d (7) { ["date"]=> string(19) "2009-02-03 12:34:41" ["timezone_type"]=> int(2) ["timezone"]=> string(3) "GMT" -} -object(DateTimeExt2)#%d (7) { ["property3"]=> bool(true) ["property4"]=> @@ -80,11 +86,5 @@ object(DateTimeExt2)#%d (7) { int(99) ["property2"]=> string(5) "Hello" - ["date"]=> - string(19) "2009-02-03 12:34:41" - ["timezone_type"]=> - int(2) - ["timezone"]=> - string(3) "GMT" } ===DONE=== diff --git a/ext/date/tests/DateTime_serialize.phpt b/ext/date/tests/DateTime_serialize.phpt index ff827360fb7fc..a7d3ba6337bb1 100644 --- a/ext/date/tests/DateTime_serialize.phpt +++ b/ext/date/tests/DateTime_serialize.phpt @@ -15,6 +15,18 @@ var_dump($date2); // Try to use unserialzied object var_dump( $date2->format( "F j, Y, g:i a") ); +$date3 = new DateTime("2013-09-22 16:43:23"); +$date3->name_day = "Darina"; +$date3->value = 23; +var_dump($date3); +$serialized = serialize($date3); +var_dump($serialized); + +$date4 = unserialize($serialized); +var_dump($date4); +// Try to use unserialzied object +var_dump( $date4->format( "F j, Y, g:i a") ); + ?> ===DONE=== --EXPECTF-- @@ -36,4 +48,30 @@ object(DateTime)#%d (3) { string(13) "Europe/London" } string(23) "July 14, 2005, 10:30 pm" +object(DateTime)#%d (5) { + ["date"]=> + string(19) "2013-09-22 16:43:23" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(13) "Europe/London" + ["name_day"]=> + string(6) "Darina" + ["value"]=> + int(23) +} +string(163) "O:8:"DateTime":5:{s:4:"date";s:19:"2013-09-22 16:43:23";s:13:"timezone_type";i:3;s:8:"timezone";s:13:"Europe/London";s:8:"name_day";s:6:"Darina";s:5:"value";i:23;}" +object(DateTime)#%d (5) { + ["date"]=> + string(19) "2013-09-22 16:43:23" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(13) "Europe/London" + ["name_day"]=> + string(6) "Darina" + ["value"]=> + int(23) +} +string(27) "September 22, 2013, 4:43 pm" ===DONE=== \ No newline at end of file diff --git a/ext/date/tests/DateTime_verify.phpt b/ext/date/tests/DateTime_verify.phpt index a03911f5c8298..14b3b68d07107 100644 --- a/ext/date/tests/DateTime_verify.phpt +++ b/ext/date/tests/DateTime_verify.phpt @@ -27,7 +27,7 @@ object(ReflectionClass)#%d (1) { string(8) "DateTime" } ..and get names of all its methods -array(18) { +array(17) { [0]=> &object(ReflectionMethod)#%d (2) { ["name"]=> @@ -36,118 +36,111 @@ array(18) { string(8) "DateTime" } [1]=> - &object(ReflectionMethod)#%d (2) { - ["name"]=> - string(8) "__wakeup" - ["class"]=> - string(8) "DateTime" - } - [2]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(11) "__set_state" ["class"]=> string(8) "DateTime" } - [3]=> + [2]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(16) "createFromFormat" ["class"]=> string(8) "DateTime" } - [4]=> + [3]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(13) "getLastErrors" ["class"]=> string(8) "DateTime" } - [5]=> + [4]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(6) "format" ["class"]=> string(8) "DateTime" } - [6]=> + [5]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(6) "modify" ["class"]=> string(8) "DateTime" } - [7]=> + [6]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(3) "add" ["class"]=> string(8) "DateTime" } - [8]=> + [7]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(3) "sub" ["class"]=> string(8) "DateTime" } - [9]=> + [8]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(11) "getTimezone" ["class"]=> string(8) "DateTime" } - [10]=> + [9]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(11) "setTimezone" ["class"]=> string(8) "DateTime" } - [11]=> + [10]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(9) "getOffset" ["class"]=> string(8) "DateTime" } - [12]=> + [11]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(7) "setTime" ["class"]=> string(8) "DateTime" } - [13]=> + [12]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(7) "setDate" ["class"]=> string(8) "DateTime" } - [14]=> + [13]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(10) "setISODate" ["class"]=> string(8) "DateTime" } - [15]=> + [14]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(12) "setTimestamp" ["class"]=> string(8) "DateTime" } - [16]=> + [15]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(12) "getTimestamp" ["class"]=> string(8) "DateTime" } - [17]=> + [16]=> &object(ReflectionMethod)#%d (2) { ["name"]=> string(4) "diff" diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index afc5f178e4fcd..78727d3923038 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -38,8 +38,47 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC); + +#define PHP_SERIALIZE_FAILURE (FAILURE) +#define PHP_SERIALIZE_CUSTOM (SUCCESS) +#define PHP_SERIALIZE_OBJECT (SUCCESS + 1) + typedef HashTable* php_serialize_data_t; +/* serialize variable */ +PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC); + +/* add object serialization string prefix */ +PHPAPI void php_var_serialize_object_start(smart_str *buf, zval *object, zend_uint nprops TSRMLS_DC); + +/* append string that ends the object definition */ +PHPAPI void php_var_serialize_object_end(smart_str *buf); + +/* append null property */ +PHPAPI void php_var_serialize_property_null(smart_str *buf, const char *key); + +/* append boolean property */ +PHPAPI void php_var_serialize_property_bool(smart_str *buf, const char *key, int value); + +/* append long property */ +PHPAPI void php_var_serialize_property_long(smart_str *buf, const char *key, long value); + +/* append double property */ +PHPAPI void php_var_serialize_property_double(smart_str *buf, const char *key, double value TSRMLS_DC); + +/* append string property */ +PHPAPI void php_var_serialize_property_string(smart_str *buf, const char *key, const char *value); + +/* append string property */ +PHPAPI void php_var_serialize_property_stringl(smart_str *buf, const char *key, const char *value, int value_len); + +/* append string property zval */ +PHPAPI void php_var_serialize_property_zval(smart_str *buf, const char *key, zval *value, zend_serialize_data *data TSRMLS_DC); + +/* append properties taken from HashTable */ +PHPAPI void php_var_serialize_properties(smart_str *buf, HashTable *properties, zend_serialize_data *data TSRMLS_DC); + + struct php_unserialize_data { void *first; void *last; @@ -49,9 +88,18 @@ struct php_unserialize_data { typedef struct php_unserialize_data* php_unserialize_data_t; -PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC); +/* unserialize variable */ PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC); +/* whether unserialization finished */ +PHPAPI int php_var_unserialize_has_properties(const unsigned char *buf, zend_uint buf_len); + +/* unserialize all properties of the serialized object and save them to ht */ +PHPAPI int php_var_unserialize_properties(HashTable *ht, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC); + +/* unserialize one property (key and value) of the serialized object */ +PHPAPI int php_var_unserialize_property(zval *key, zval *value, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC); + #define PHP_VAR_SERIALIZE_INIT(var_hash_ptr) \ do { \ /* fprintf(stderr, "SERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */ \ diff --git a/ext/standard/var.c b/ext/standard/var.c index 5d104a90cfee8..9f9cd65f3450a 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -420,6 +420,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) int tmp_len, tmp_len2; const char *class_name; zend_uint class_name_len; + int is_temp; switch (Z_TYPE_PP(struc)) { case IS_BOOL: @@ -473,7 +474,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) break; case IS_OBJECT: - myht = Z_OBJPROP_PP(struc); + myht = Z_OBJDEBUG_PP(struc, is_temp); if(myht && myht->nApplyCount > 0){ smart_str_appendl(buf, "NULL", 4); zend_error(E_WARNING, "var_export does not handle circular references"); @@ -491,6 +492,11 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) efree((char*)class_name); if (myht) { zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_object_element_export, 1, level, buf); + + if (is_temp) { + zend_hash_destroy(myht); + efree(myht); + } } if (level > 1) { buffer_append_spaces(buf, level - 1); @@ -582,6 +588,20 @@ static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old } /* }}} */ +static inline void php_var_serialize_null(smart_str *buf) /* {{{ */ +{ + smart_str_appendl(buf, "N;", 2); +} +/* }}} */ + +static inline void php_var_serialize_bool(smart_str *buf, long val) /* {{{ */ +{ + smart_str_appendl(buf, "b:", 2); + smart_str_append_long(buf, val); + smart_str_appendc(buf, ';'); +} +/* }}} */ + static inline void php_var_serialize_long(smart_str *buf, long val) /* {{{ */ { smart_str_appendl(buf, "i:", 2); @@ -590,7 +610,21 @@ static inline void php_var_serialize_long(smart_str *buf, long val) /* {{{ */ } /* }}} */ -static inline void php_var_serialize_string(smart_str *buf, char *str, int len) /* {{{ */ +static inline void php_var_serialize_double(smart_str *buf, double val TSRMLS_DC) /* {{{ */ +{ + char *s; + + smart_str_appendl(buf, "d:", 2); + s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1); + php_gcvt(val, PG(serialize_precision), '.', 'E', s); + smart_str_appends(buf, s); + smart_str_appendc(buf, ';'); + efree(s); + return; +} +/* }}} */ + +static inline void php_var_serialize_string(smart_str *buf, const char *str, int len) /* {{{ */ { smart_str_appendl(buf, "s:", 2); smart_str_append_long(buf, len); @@ -600,6 +634,55 @@ static inline void php_var_serialize_string(smart_str *buf, char *str, int len) } /* }}} */ +static inline void php_var_serialize_hash_table(smart_str *buf, HashTable *myht, zval **pstruc, zend_bool incomplete_class, HashTable *var_hash TSRMLS_DC) /* {{{ */ +{ + int i; + char *key; + zval **data; + ulong index; + uint key_len; + HashPosition pos; + + zend_hash_internal_pointer_reset_ex(myht, &pos); + for (;; zend_hash_move_forward_ex(myht, &pos)) { + i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); + if (i == HASH_KEY_NON_EXISTENT) { + break; + } + if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { + continue; + } + + switch (i) { + case HASH_KEY_IS_LONG: + php_var_serialize_long(buf, index); + break; + case HASH_KEY_IS_STRING: + php_var_serialize_string(buf, key, key_len - 1); + break; + } + + /* we should still add element even if it's not OK, + * since we already wrote the length of the array before */ + if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) != SUCCESS + || !data + || data == pstruc + || (Z_TYPE_PP(data) == IS_ARRAY && Z_ARRVAL_PP(data)->nApplyCount > 1) + ) { + smart_str_appendl(buf, "N;", 2); + } else { + if (Z_TYPE_PP(data) == IS_ARRAY) { + Z_ARRVAL_PP(data)->nApplyCount++; + } + php_var_serialize_intern(buf, *data, var_hash TSRMLS_CC); + if (Z_TYPE_PP(data) == IS_ARRAY) { + Z_ARRVAL_PP(data)->nApplyCount--; + } + } + } +} +/* }}} */ + static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc TSRMLS_DC) /* {{{ */ { PHP_CLASS_ATTRIBUTES; @@ -732,30 +815,20 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var switch (Z_TYPE_P(struc)) { case IS_BOOL: - smart_str_appendl(buf, "b:", 2); - smart_str_append_long(buf, Z_LVAL_P(struc)); - smart_str_appendc(buf, ';'); + php_var_serialize_bool(buf, Z_LVAL_P(struc)); return; case IS_NULL: - smart_str_appendl(buf, "N;", 2); + php_var_serialize_null(buf); return; case IS_LONG: php_var_serialize_long(buf, Z_LVAL_P(struc)); return; - case IS_DOUBLE: { - char *s; - - smart_str_appendl(buf, "d:", 2); - s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1); - php_gcvt(Z_DVAL_P(struc), PG(serialize_precision), '.', 'E', s); - smart_str_appends(buf, s); - smart_str_appendc(buf, ';'); - efree(s); - return; - } + case IS_DOUBLE: + php_var_serialize_double(buf, Z_DVAL_P(struc) TSRMLS_CC); + return; case IS_STRING: php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc)); @@ -764,7 +837,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var case IS_OBJECT: { zval *retval_ptr = NULL; zval fname; - int res; + int res, serialize_rc; zend_class_entry *ce = NULL; if (Z_OBJ_HT_P(struc)->get_class_entry) { @@ -776,7 +849,8 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var unsigned char *serialized_data = NULL; zend_uint serialized_length; - if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash TSRMLS_CC) == SUCCESS) { + serialize_rc = ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash TSRMLS_CC); + if (serialize_rc == PHP_SERIALIZE_CUSTOM) { smart_str_appendl(buf, "C:", 2); smart_str_append_long(buf, (int)Z_OBJCE_P(struc)->name_length); smart_str_appendl(buf, ":\"", 2); @@ -787,6 +861,8 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var smart_str_appendl(buf, ":{", 2); smart_str_appendl(buf, serialized_data, serialized_length); smart_str_appendc(buf, '}'); + } else if (serialize_rc == PHP_SERIALIZE_OBJECT) { + smart_str_appendl(buf, serialized_data, serialized_length); } else { smart_str_appendl(buf, "N;", 2); } @@ -849,49 +925,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var smart_str_append_long(buf, i); smart_str_appendl(buf, ":{", 2); if (i > 0) { - char *key; - zval **data; - ulong index; - uint key_len; - HashPosition pos; - - zend_hash_internal_pointer_reset_ex(myht, &pos); - for (;; zend_hash_move_forward_ex(myht, &pos)) { - i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos); - if (i == HASH_KEY_NON_EXISTENT) { - break; - } - if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) { - continue; - } - - switch (i) { - case HASH_KEY_IS_LONG: - php_var_serialize_long(buf, index); - break; - case HASH_KEY_IS_STRING: - php_var_serialize_string(buf, key, key_len - 1); - break; - } - - /* we should still add element even if it's not OK, - * since we already wrote the length of the array before */ - if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) != SUCCESS - || !data - || data == &struc - || (Z_TYPE_PP(data) == IS_ARRAY && Z_ARRVAL_PP(data)->nApplyCount > 1) - ) { - smart_str_appendl(buf, "N;", 2); - } else { - if (Z_TYPE_PP(data) == IS_ARRAY) { - Z_ARRVAL_PP(data)->nApplyCount++; - } - php_var_serialize_intern(buf, *data, var_hash TSRMLS_CC); - if (Z_TYPE_PP(data) == IS_ARRAY) { - Z_ARRVAL_PP(data)->nApplyCount--; - } - } - } + php_var_serialize_hash_table(buf, myht, &struc, incomplete_class, var_hash TSRMLS_CC); } smart_str_appendc(buf, '}'); return; @@ -910,6 +944,75 @@ PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t } /* }}} */ +PHPAPI void php_var_serialize_object_start(smart_str *buf, zval *object, zend_uint nprops TSRMLS_DC) /* {{{ */ +{ + php_var_serialize_class_name(buf, object TSRMLS_CC); + smart_str_append_long(buf, nprops); + smart_str_appendl(buf, ":{", 2); +} +/* }}} */ + +PHPAPI void php_var_serialize_object_end(smart_str *buf) /* {{{ */ +{ + smart_str_appendc(buf, '}'); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_null(smart_str *buf, const char *key) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_bool(smart_str *buf, const char *key, int value) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_bool(buf, (long) value); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_long(smart_str *buf, const char *key, long value) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_long(buf, value); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_double(smart_str *buf, const char *key, double value TSRMLS_DC) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_double(buf, value TSRMLS_CC); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_string(smart_str *buf, const char *key, const char *value) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_string(buf, value, strlen(value)); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_stringl(smart_str *buf, const char *key, const char *value, int value_len) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_string(buf, value, value_len); +} +/* }}} */ + +PHPAPI void php_var_serialize_property_zval(smart_str *buf, const char *key, zval *value, zend_serialize_data *data TSRMLS_DC) /* {{{ */ +{ + php_var_serialize_string(buf, key, strlen(key)); + php_var_serialize_intern(buf, value, (HashTable *) data TSRMLS_CC); +} +/* }}} */ + +PHPAPI void php_var_serialize_properties(smart_str *buf, HashTable *properties, zend_serialize_data *data TSRMLS_DC) /* {{{ */ +{ + php_var_serialize_hash_table(buf, properties, NULL, 0, (HashTable *) data TSRMLS_CC); +} +/* }}} */ + + /* {{{ proto string serialize(mixed variable) Returns a string representation of variable (which can later be unserialized) */ PHP_FUNCTION(serialize) diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index f7546428b1e61..3d1b8e4a7bec2 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.6 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -23,6 +23,7 @@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" +#include "zend_interfaces.h" /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@ -226,7 +227,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen #define YYMARKER marker -#line 234 "ext/standard/var_unserializer.re" +#line 235 "ext/standard/var_unserializer.re" @@ -379,7 +380,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ce->name); object_init_ex(*rval, ce); - } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) { + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash TSRMLS_CC) < 0) { return 0; } @@ -436,6 +437,78 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif +PHPAPI int php_var_unserialize_has_properties(const unsigned char *buf, zend_uint buf_len) +{ + return buf_len && *buf != '}'; +} + +PHPAPI int php_var_unserialize_properties(HashTable *ht, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + while (php_var_unserialize_has_properties(*buf, *buf_len)) { + zval *key, *value; + + ALLOC_INIT_ZVAL(key); + + if (!php_var_unserialize(&key, buf, max, NULL TSRMLS_CC)) { + zval_dtor(key); + FREE_ZVAL(key); + return 0; + } + + if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { + zval_dtor(key); + FREE_ZVAL(key); + return 0; + } + + ALLOC_INIT_ZVAL(value); + + if (!php_var_unserialize(&value, buf, max, (php_unserialize_data_t *) data TSRMLS_CC)) { + zval_dtor(key); + FREE_ZVAL(key); + zval_dtor(value); + FREE_ZVAL(value); + return 0; + } + + zend_hash_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof value, NULL); + + zval_dtor(key); + FREE_ZVAL(key); + + if (php_var_unserialize_has_properties(*buf, *buf_len) && *(*buf-1) != ';' && *(*buf-1) != '}') { + (*buf)--; + return 0; + } + } + + *buf_len = max - *buf; + return 1; +} + +PHPAPI int php_var_unserialize_property(zval *key, zval *value, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + if (!php_var_unserialize(&key, buf, max, NULL TSRMLS_CC) || Z_TYPE_P(key) != IS_STRING) { + zval_dtor(key); + return 0; + } + if (!php_var_unserialize(&value, buf, max, (php_unserialize_data_t *) data TSRMLS_CC)) { + zval_dtor(key); + zval_dtor(value); + return 0; + } + *buf_len = max - *buf; + return 1; +} + PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; @@ -457,7 +530,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 461 "ext/standard/var_unserializer.c" +#line 534 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -517,9 +590,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 812 "ext/standard/var_unserializer.re" +#line 896 "ext/standard/var_unserializer.re" { return 0; } -#line 523 "ext/standard/var_unserializer.c" +#line 596 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -562,13 +635,13 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) goto yy3; yy14: ++YYCURSOR; -#line 806 "ext/standard/var_unserializer.re" +#line 890 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 572 "ext/standard/var_unserializer.c" +#line 645 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -598,7 +671,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 660 "ext/standard/var_unserializer.re" +#line 733 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -742,9 +815,20 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } efree(class_name); + /* custom unserialization for objects that are not custom (C: prefix) */ + if (ce->unserialize && ce->unserialize != zend_user_unserialize) { + int ret = ce->unserialize(rval, ce, (const unsigned char*)*p, max - *p, (zend_unserialize_data *)var_hash TSRMLS_CC); + if (ret < 0) { + return 0; + } + /* In this case the return value from unserialize callback is the number of character left in the buffer */ + (*p) = max - ret; + return finish_nested_data(UNSERIALIZE_PASSTHRU); + } + return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 748 "ext/standard/var_unserializer.c" +#line 832 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -769,7 +853,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 652 "ext/standard/var_unserializer.re" +#line 725 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -777,7 +861,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 781 "ext/standard/var_unserializer.c" +#line 865 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -798,7 +882,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 632 "ext/standard/var_unserializer.re" +#line 705 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -818,7 +902,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 822 "ext/standard/var_unserializer.c" +#line 906 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -839,7 +923,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 603 "ext/standard/var_unserializer.re" +#line 676 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -868,7 +952,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 872 "ext/standard/var_unserializer.c" +#line 956 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -889,7 +973,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 575 "ext/standard/var_unserializer.re" +#line 648 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -917,7 +1001,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 921 "ext/standard/var_unserializer.c" +#line 1005 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1005,7 +1089,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } yy63: ++YYCURSOR; -#line 565 "ext/standard/var_unserializer.re" +#line 638 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1015,7 +1099,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1019 "ext/standard/var_unserializer.c" +#line 1103 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1074,22 +1158,22 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 550 "ext/standard/var_unserializer.re" +#line 623 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); - if (!strncmp(start + 2, "NAN", 3)) { + if (!strncmp((const char *) (start + 2), "NAN", 3)) { ZVAL_DOUBLE(*rval, php_get_nan()); - } else if (!strncmp(start + 2, "INF", 3)) { + } else if (!strncmp((const char *) (start + 2), "INF", 3)) { ZVAL_DOUBLE(*rval, php_get_inf()); - } else if (!strncmp(start + 2, "-INF", 4)) { + } else if (!strncmp((const char *) (start + 2), "-INF", 4)) { ZVAL_DOUBLE(*rval, -php_get_inf()); } return 1; } -#line 1093 "ext/standard/var_unserializer.c" +#line 1177 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1116,7 +1200,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 523 "ext/standard/var_unserializer.re" +#line 596 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1143,7 +1227,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1147 "ext/standard/var_unserializer.c" +#line 1231 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1151,24 +1235,24 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 516 "ext/standard/var_unserializer.re" +#line 589 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1162 "ext/standard/var_unserializer.c" +#line 1246 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 509 "ext/standard/var_unserializer.re" +#line 582 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1172 "ext/standard/var_unserializer.c" +#line 1256 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1191,7 +1275,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 486 "ext/standard/var_unserializer.re" +#line 559 "ext/standard/var_unserializer.re" { long id; @@ -1214,7 +1298,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1218 "ext/standard/var_unserializer.c" +#line 1302 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1237,7 +1321,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 465 "ext/standard/var_unserializer.re" +#line 538 "ext/standard/var_unserializer.re" { long id; @@ -1258,9 +1342,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1262 "ext/standard/var_unserializer.c" +#line 1346 "ext/standard/var_unserializer.c" } -#line 814 "ext/standard/var_unserializer.re" +#line 898 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 76c501e1b58c9..45914bf50418d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -21,6 +21,7 @@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" +#include "zend_interfaces.h" /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@ -383,7 +384,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) if (ce->unserialize == NULL) { zend_error(E_WARNING, "Class %s has no unserializer", ce->name); object_init_ex(*rval, ce); - } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash TSRMLS_CC) != SUCCESS) { + } else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash TSRMLS_CC) < 0) { return 0; } @@ -440,6 +441,78 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif +PHPAPI int php_var_unserialize_has_properties(const unsigned char *buf, zend_uint buf_len) +{ + return buf_len && *buf != '}'; +} + +PHPAPI int php_var_unserialize_properties(HashTable *ht, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + while (php_var_unserialize_has_properties(*buf, *buf_len)) { + zval *key, *value; + + ALLOC_INIT_ZVAL(key); + + if (!php_var_unserialize(&key, buf, max, NULL TSRMLS_CC)) { + zval_dtor(key); + FREE_ZVAL(key); + return 0; + } + + if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { + zval_dtor(key); + FREE_ZVAL(key); + return 0; + } + + ALLOC_INIT_ZVAL(value); + + if (!php_var_unserialize(&value, buf, max, (php_unserialize_data_t *) data TSRMLS_CC)) { + zval_dtor(key); + FREE_ZVAL(key); + zval_dtor(value); + FREE_ZVAL(value); + return 0; + } + + zend_hash_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof value, NULL); + + zval_dtor(key); + FREE_ZVAL(key); + + if (php_var_unserialize_has_properties(*buf, *buf_len) && *(*buf-1) != ';' && *(*buf-1) != '}') { + (*buf)--; + return 0; + } + } + + *buf_len = max - *buf; + return 1; +} + +PHPAPI int php_var_unserialize_property(zval *key, zval *value, const unsigned char **buf, zend_uint *buf_len, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + if (!php_var_unserialize(&key, buf, max, NULL TSRMLS_CC) || Z_TYPE_P(key) != IS_STRING) { + zval_dtor(key); + return 0; + } + if (!php_var_unserialize(&value, buf, max, (php_unserialize_data_t *) data TSRMLS_CC)) { + zval_dtor(key); + zval_dtor(value); + return 0; + } + *buf_len = max - *buf; + return 1; +} + PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; @@ -551,11 +624,11 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *p = YYCURSOR; INIT_PZVAL(*rval); - if (!strncmp(start + 2, "NAN", 3)) { + if (!strncmp((const char *) (start + 2), "NAN", 3)) { ZVAL_DOUBLE(*rval, php_get_nan()); - } else if (!strncmp(start + 2, "INF", 3)) { + } else if (!strncmp((const char *) (start + 2), "INF", 3)) { ZVAL_DOUBLE(*rval, php_get_inf()); - } else if (!strncmp(start + 2, "-INF", 4)) { + } else if (!strncmp((const char *) (start + 2), "-INF", 4)) { ZVAL_DOUBLE(*rval, -php_get_inf()); } @@ -800,6 +873,17 @@ object ":" uiv ":" ["] { } efree(class_name); + /* custom unserialization for objects that are not custom (C: prefix) */ + if (ce->unserialize && ce->unserialize != zend_user_unserialize) { + int ret = ce->unserialize(rval, ce, (const unsigned char*)*p, max - *p, (zend_unserialize_data *)var_hash TSRMLS_CC); + if (ret < 0) { + return 0; + } + /* In this case the return value from unserialize callback is the number of character left in the buffer */ + (*p) = max - ret; + return finish_nested_data(UNSERIALIZE_PASSTHRU); + } + return object_common2(UNSERIALIZE_PASSTHRU, elements); }