From 472e08577d3726d95101b7c492d2ce26bef8d5f9 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 15 Sep 2013 19:36:58 +0100 Subject: [PATCH] Add new internal serialize API --- ext/standard/php_var.h | 48 ++++++- ext/standard/var.c | 219 ++++++++++++++++++++++--------- ext/standard/var_unserializer.c | 151 ++++++++++++++++----- ext/standard/var_unserializer.re | 87 +++++++++++- 4 files changed, 403 insertions(+), 102 deletions(-) diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index afc5f178e4fcd..a6417b5b4c3b0 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,16 @@ 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); +/* 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); + +/* 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_uint elements, 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 c1e7c2f3ee346..4194873d57ccf 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -584,6 +584,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); @@ -592,7 +606,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); @@ -602,6 +630,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; @@ -734,30 +811,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)); @@ -766,7 +833,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) { @@ -778,7 +845,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); @@ -789,6 +857,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); } @@ -851,49 +921,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; @@ -912,6 +940,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..d8ace9591ceab 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,73 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif +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_properties(HashTable *ht, const unsigned char **buf, zend_uint *buf_len, zend_uint elements, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + while (elements-- > 0) { + 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 (elements && *(*buf-1) != ';' && *(*buf-1) != '}') { + (*buf)--; + return 0; + } + } + + *buf_len = max - *buf; + return 1; +} + PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; @@ -457,7 +525,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 461 "ext/standard/var_unserializer.c" +#line 529 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -517,9 +585,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 812 "ext/standard/var_unserializer.re" +#line 891 "ext/standard/var_unserializer.re" { return 0; } -#line 523 "ext/standard/var_unserializer.c" +#line 591 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -562,13 +630,13 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) goto yy3; yy14: ++YYCURSOR; -#line 806 "ext/standard/var_unserializer.re" +#line 885 "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 640 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -598,7 +666,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 660 "ext/standard/var_unserializer.re" +#line 728 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -742,9 +810,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 827 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -769,7 +848,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 652 "ext/standard/var_unserializer.re" +#line 720 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -777,7 +856,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 860 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -798,7 +877,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 632 "ext/standard/var_unserializer.re" +#line 700 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -818,7 +897,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 822 "ext/standard/var_unserializer.c" +#line 901 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -839,7 +918,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 603 "ext/standard/var_unserializer.re" +#line 671 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -868,7 +947,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 872 "ext/standard/var_unserializer.c" +#line 951 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -889,7 +968,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 575 "ext/standard/var_unserializer.re" +#line 643 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -917,7 +996,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 921 "ext/standard/var_unserializer.c" +#line 1000 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1005,7 +1084,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } yy63: ++YYCURSOR; -#line 565 "ext/standard/var_unserializer.re" +#line 633 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1015,7 +1094,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 1098 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1074,22 +1153,22 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 550 "ext/standard/var_unserializer.re" +#line 618 "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 1172 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1116,7 +1195,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 591 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1143,7 +1222,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 1226 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1151,24 +1230,24 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 516 "ext/standard/var_unserializer.re" +#line 584 "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 1241 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 509 "ext/standard/var_unserializer.re" +#line 577 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1172 "ext/standard/var_unserializer.c" +#line 1251 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1191,7 +1270,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 554 "ext/standard/var_unserializer.re" { long id; @@ -1214,7 +1293,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1218 "ext/standard/var_unserializer.c" +#line 1297 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1237,7 +1316,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 533 "ext/standard/var_unserializer.re" { long id; @@ -1258,9 +1337,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1262 "ext/standard/var_unserializer.c" +#line 1341 "ext/standard/var_unserializer.c" } -#line 814 "ext/standard/var_unserializer.re" +#line 893 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 76c501e1b58c9..0727077a3b7e9 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,73 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif +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_properties(HashTable *ht, const unsigned char **buf, zend_uint *buf_len, zend_uint elements, zend_unserialize_data *data TSRMLS_DC) +{ + const unsigned char *max; + + max = *buf + *buf_len; + + while (elements-- > 0) { + 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 (elements && *(*buf-1) != ';' && *(*buf-1) != '}') { + (*buf)--; + return 0; + } + } + + *buf_len = max - *buf; + return 1; +} + PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; @@ -551,11 +619,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 +868,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); }