├── .gitignore ├── AUTHORS ├── COPYRIGHT ├── HISTORY ├── README ├── README.md ├── TODO ├── etc └── git │ └── hooks │ └── pre-commit ├── make.sh ├── rockspec ├── lua-phpserialize-0.0.1-1.rockspec ├── lua-phpserialize-0.0.2-1.rockspec └── lua-phpserialize-scm-1.rockspec ├── src ├── lua-phpserialize.c ├── lua-phpserialize.def ├── util.c └── util.h └── test └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .*~ 3 | \#*\# 4 | .\#*\# 5 | 6 | .DS_Store 7 | .project 8 | .settings 9 | 10 | /bin/ 11 | /include/ 12 | /lib/ 13 | /obj/ 14 | /tmp/ 15 | 16 | *.o 17 | *.so 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | lua-phpserialize authors: 2 | ------------------------- 3 | 4 | Alexander Gladysh 5 | Ilia Razinkov 6 | Alexander Fedora 7 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | lua-phpserialize License 2 | ------------------------ 3 | 4 | lua-phpserialize is licensed under the terms of the MIT license reproduced 5 | below. This means that luabins is free software and can be used for both 6 | academic and commercial purposes at absolutely no cost. 7 | 8 | =============================================================================== 9 | 10 | Copyright (C) 2008-2009 lua-phpserialize authors 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of this software and associated documentation files (the "Software"), to deal 14 | in the Software without restriction, including without limitation the rights 15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the Software is 17 | furnished to do so, subject to the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be included in 20 | all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | THE SOFTWARE. 29 | 30 | =============================================================================== 31 | 32 | (end of COPYRIGHT) 33 | -------------------------------------------------------------------------------- /HISTORY: -------------------------------------------------------------------------------- 1 | v0.0.2 2 | ====== 3 | 4 | Bugfix release. 5 | 6 | -- Better handling of huge tables (thanks to Alexander Fedora). 7 | 8 | v0.0.1 9 | ====== 10 | 11 | Initial release. 12 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lua-phpserialize: A library to work with PHP serialize() format 2 | =============================================================== 3 | 4 | phpserialize.phpserialize() 5 | --------------------------- 6 | 7 | `phpserialize.phpserialize(value, [php_base_index]) : string or nil, err` 8 | 9 | Parameters: 10 | 11 | * `value`: value to be serialized 12 | * `php_base_index`: base index for resulting PHP arrays (if any). 13 | Optional, default: 1. 14 | 15 | Serializes primitive Lua values (including tables) to format, 16 | understandable by PHP's `unserialize()` function. 17 | 18 | Returns string with serialized data or `nil` and error message. 19 | 20 | Serializable Lua types with PHP counterparts: 21 | 22 | `nil` => `null` 23 | `boolean` => `bool` 24 | `number` (integers) => `integer` 25 | `number` (non-integers, except `-NaN`, `-inf`, `+inf`) => `double` 26 | `table` => `array` (but see below) 27 | 28 | Non-serializable Lua types: 29 | 30 | `function` (would fail) 31 | `coroutine` (would fail) 32 | `userdata` / `lightuserdata` (would fail) 33 | numbers `NaN`, `-inf`, `+inf` (would fail) 34 | 35 | Serializable table key types: 36 | 37 | `number` (integers only!) => `integer` 38 | `string` (not representing integers only!) => `string` 39 | 40 | Any other key type (including non-integer numbers and 41 | strings, convertible to integers, see below) would fail. 42 | 43 | Table value may be of any of serializable Lua types (see above). 44 | 45 | Notes on serialization of tables: 46 | --------------------------------- 47 | 48 | This function does NOT handle fancy stuff like metatables (ignored), 49 | weak table keys/values (treated as normal) and recursive tables 50 | (would fail). Non-recursive tables deeper that `PHPSERIALIZE_MAXNESTING` 51 | (default 128) are also can't be serialized. 52 | 53 | Due to limitations of PHP's array type, table keys may be strings 54 | or integers only. PHP forbids string keys convertible to integers -- 55 | such keys are automatically converted to integers at array creation. 56 | To force unambigous serialization, non-integer numeric keys and 57 | string keys, convertible to integers are forbidden. 58 | 59 | Note that it is not possible in Lua to have `nil` as table key. 60 | 61 | See the copyright information in the file named `COPYRIGHT`. 62 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | -- Fix tests under LuaJIT 2 (see TODO in test.lua) 2 | -- Add proper Makefile 3 | -- Add a framework to run test conveniently (use one from lua-nucleo) 4 | -- See TODOs in lua-phpserialize.c 5 | -- Code must fit in 80 chars width 6 | -- Reuse code from lua-nucleo in util.{c,h} 7 | -- Prepend all preprocessor definitions (except L* checkers) with LPHPSERIALIZE_ 8 | -- Backport some good practice from luabins. 9 | -- That is_float_finite definition is a bit scary. Think out more a legal way. 10 | -------------------------------------------------------------------------------- /etc/git/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if git-rev-parse --verify HEAD 2>/dev/null 4 | then 5 | against=HEAD 6 | else 7 | # Initial commit: diff against an empty tree object 8 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 9 | fi 10 | 11 | git diff-index --check --cached $against -- || exit 1 12 | 13 | # check lua syntax 14 | for i in `git diff-index --cached HEAD | egrep "\\.lua$" | cut -d " " -f 4`; do 15 | git show $i | luac -p - || exit 1 16 | done 17 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Using luarocks make command 4 | # Note this installs the rock locally 5 | 6 | luarocks make rockspec/lua-phpserialize-scm-1.rockspec 7 | -------------------------------------------------------------------------------- /rockspec/lua-phpserialize-0.0.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-phpserialize" 2 | version = "0.0.1-1" 3 | 4 | source = { 5 | url = "http://github.com/agladysh/lua-phpserialize/tarball/v0.0.1", 6 | } 7 | 8 | description = { 9 | summary = "Lua module to support PHP serialize()", 10 | detailed = [[ 11 | lua-phpserialize: A module to work with PHP serialize() format. 12 | ]], 13 | homepage = "http://github.com/agladysh/lua-phpserialize", 14 | license = "MIT/X11" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | phpserialize = { 25 | sources = { 26 | "src/lua-phpserialize.c", 27 | "src/util.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rockspec/lua-phpserialize-0.0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-phpserialize" 2 | version = "0.0.2-1" 3 | 4 | source = { 5 | url = "http://github.com/agladysh/lua-phpserialize/tarball/v0.0.2", 6 | } 7 | 8 | description = { 9 | summary = "Lua module to support PHP serialize()", 10 | detailed = [[ 11 | lua-phpserialize: A module to work with PHP serialize() format. 12 | ]], 13 | homepage = "http://github.com/agladysh/lua-phpserialize", 14 | license = "MIT/X11" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | phpserialize = { 25 | sources = { 26 | "src/lua-phpserialize.c", 27 | "src/util.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rockspec/lua-phpserialize-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-phpserialize" 2 | version = "scm-1" 3 | 4 | source = { 5 | url = "git://github.com/agladysh/lua-phpserialize.git" 6 | } 7 | 8 | description = { 9 | summary = "Lua module to support PHP serialize()", 10 | detailed = [[ 11 | lua-phpserialize: A module to work with PHP serialize() format. 12 | ]], 13 | homepage = "http://github.com/agladysh/lua-phpserialize", 14 | license = "MIT/X11" 15 | } 16 | 17 | dependencies = { 18 | "lua >= 5.1" 19 | } 20 | 21 | build = { 22 | type = "builtin", 23 | modules = { 24 | phpserialize = { 25 | sources = { 26 | "src/lua-phpserialize.c", 27 | "src/util.c" 28 | }, 29 | incdirs = { 30 | "src/" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lua-phpserialize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * lphpserialize.c: Lua support for PHP serialize() 3 | * This file is a part of lua-phpserialize library. 4 | * Copyright (c) lua-phpserialize authors (see file `COPYRIGHT` for the license) 5 | */ 6 | 7 | #include "lua.h" 8 | #include "lauxlib.h" 9 | 10 | #include "util.h" 11 | 12 | /* TODO: Write non-recursive implementation */ 13 | #define PHPSERIALIZE_MAXNESTING (128) /* Maximum Lua table nesting phpserialize() can handle */ 14 | #define PHPSERIALIZE_FLOATPRECISION_STR "55" /* Default value in PHP for float serialization */ 15 | 16 | #ifndef PHPSERIALIZE_64BIT 17 | /* Bigger keys causing issues in PHP (at least in 5.2.6-2ubuntu4) */ 18 | /* Values are not exact (from higher digits) */ 19 | #define PHPSERIALIZE_MAXARRAYINTKEY ( 2110000001) 20 | #define PHPSERIALIZE_MINARRAYINTKEY (-2140000001) 21 | #else 22 | #error "TODO: Find MIN/MAX values for 64bit platforms"; 23 | #endif 24 | 25 | /* Arbitrary number of used stack slots to trigger preliminary concatenation */ 26 | /* TODO: Should be dependent on LUAI_MAXCSTACK? */ 27 | #define PHPSERIALIZE_CONCATTHRESHOLD (1024) 28 | 29 | 30 | enum SerializeStringID 31 | { 32 | SSI_NULL = 1, 33 | SSI_INTEGER = 2, 34 | SSI_DOUBLE = 3, 35 | SSI_BOOL_TRUE = 4, 36 | SSI_BOOL_FALSE = 5, 37 | SSI_STRING_BEGIN = 6, 38 | SSI_STRING_MIDDLE = 7, 39 | SSI_STRING_END = 8, 40 | SSI_ARRAY_BEGIN = 9, 41 | SSI_ARRAY_MIDDLE = 10, 42 | SSI_ARRAY_END = 11, 43 | SSI_SEMICOLON = 12, 44 | 45 | NUM_SSI = 12 /* Note IDs are one-based */ 46 | }; 47 | 48 | const char * const g_SerializeStrings[NUM_SSI + 1] = 49 | { 50 | "", /* Placeholder to be one-based */ 51 | "N;", /* SSI_NULL */ 52 | "i:", /* SSI_INTEGER */ 53 | "d:", /* SSI_DOUBLE */ 54 | "b:1;", /* SSI_BOOL_TRUE */ 55 | "b:0;", /* SSI_BOOL_FALSE */ 56 | "s:", /* SSI_STRING_BEGIN */ 57 | ":\"", /* SSI_STRING_MIDDLE */ 58 | "\";", /* SSI_STRING_END */ 59 | "a:", /* SSI_ARRAY_BEGIN */ 60 | ":{", /* SSI_ARRAY_MIDDLE */ 61 | "}", /* SSI_ARRAY_END */ 62 | ";" /* SSI_SEMICOLON */ 63 | }; 64 | 65 | /* phpserialize 66 | * ============ 67 | * 68 | * Serializes primitive Lua values (including tables) to format, 69 | * understandable by PHP's unserialize() function. 70 | * 71 | * Returns string with serialized data or nil and error message. 72 | * 73 | * Serializable Lua types with PHP counterparts: 74 | * 75 | * nil => null 76 | * boolean => bool 77 | * number (integers) => integer 78 | * number (non-integers, except -NaN, -inf, +inf) => double 79 | * table => array (but see below) 80 | * 81 | * Non-serializable Lua types: 82 | * 83 | * function (would fail) 84 | * coroutine (would fail) 85 | * userdata/lightuserdata (would fail) 86 | * numbers NaN, -inf, +inf (would fail) 87 | * 88 | * Serializable table key types: 89 | * 90 | * number (integers only!) => integer 91 | * string (not representing integers only!) => string 92 | * 93 | * Any other key type (including non-integer numbers and 94 | * strings, convertible to integers, see below) would fail. 95 | * 96 | * Table value may be of any of serializable Lua types (see above). 97 | * 98 | * Notes on serialization of tables: 99 | * --------------------------------- 100 | * 101 | * This function does NOT handle fancy stuff like metatables (ignored), 102 | * weak table keys/values (treated as normal) and recursive tables 103 | * (would fail). Non-recursive tables deeper that PHPSERIALIZE_MAXNESTING 104 | * (default 128) are also can't be serialized. 105 | * 106 | * Due to limitations of PHP's array type, table keys may be strings 107 | * or integers only. PHP forbids string keys convertible to integers -- 108 | * such keys are automatically converted to integers at array creation. 109 | * To force unambigous serialization, non-integer numeric keys and 110 | * string keys, convertible to integers are forbidden. 111 | * 112 | * Note that it is not possible in Lua to have nil as table key. 113 | */ 114 | 115 | /* If retain is 1, retains the top element on stack (slow) */ 116 | static void maybe_concat(lua_State * L, int base, int retain) 117 | { 118 | int top = lua_gettop(L); 119 | if (top - base >= PHPSERIALIZE_CONCATTHRESHOLD) 120 | { 121 | if (retain) 122 | { 123 | lua_insert(L, base); 124 | } 125 | 126 | lua_concat(L, top - base); 127 | 128 | if (retain) 129 | { 130 | /* swap result with retained element */ 131 | lua_pushvalue(L, -2); 132 | lua_remove(L, -3); 133 | } 134 | } 135 | } 136 | 137 | /* Returns 0 if string looks like integer */ 138 | static int Plookslikeinteger(const char * str, const size_t len) 139 | { 140 | /* TODO: Looks like we would have problems with unicode here */ 141 | int notguilty = 0; 142 | size_t i = 0; 143 | if (len > 1 && str[0] == '0') /* PHP does not honor string integer keys with leading zeroes as integers */ 144 | { 145 | notguilty = 1; 146 | } 147 | else 148 | { 149 | for (i = 0; i < len; ++i) 150 | { 151 | if (str[i] < '0' || str[i] > '9') 152 | { 153 | notguilty = 1; 154 | break; 155 | } 156 | } 157 | } 158 | 159 | return notguilty; 160 | } 161 | 162 | /* 163 | * Warning: Index must be absolute! For example, you must use lua_gettop() instead of -1. 164 | * Note: Upvalues referenced here are belong to Cphpserialize function below. 165 | * Note: This function does not clean up stack after itself 166 | */ 167 | static int Pphpserializekey(lua_State * L, int index, int php_base_index) 168 | { 169 | int type = lua_type(L, index); 170 | int error = 0; 171 | 172 | luaL_checkstack(L, 5, "serializekey"); /* Arbitrary value */ 173 | 174 | switch (type) 175 | { 176 | case LUA_TNUMBER: 177 | { 178 | /* TODO: Ensure this is correct check for number being integer. Check corner cases (math.huge, NaN, epsilon, etc.)! */ 179 | /* TODO: Filter out NaN, -inf, +inf etc. */ 180 | lua_Number number = lua_tonumber(L, index); 181 | if (number != (lua_Integer)number || !is_float_finite(number) ) 182 | { 183 | lua_pushnil(L); 184 | lua_pushfstring(L, "non-integer numeric keys are not supported"); 185 | error = 1; 186 | } 187 | else if (number > PHPSERIALIZE_MAXARRAYINTKEY || number < PHPSERIALIZE_MINARRAYINTKEY) 188 | { 189 | lua_pushnil(L); 190 | lua_pushfstring(L, "integer key too big/too small"); 191 | error = 1; 192 | } 193 | else 194 | { 195 | char big_integer[256] = {0}; 196 | 197 | lua_Integer number = lua_tointeger(L, index); 198 | number = number + (php_base_index - 1); 199 | lua_pushvalue(L, lua_upvalueindex(SSI_INTEGER)); 200 | 201 | /* instead of using lua`s tostring we make conversion by hand. This will overcome 10000... -> 1e14 issue */ 202 | /* was: lua_pushinteger(L, number);lua_tostring(L, -1);*/ 203 | /*snprintf(big_integer, sizeof(big_integer), "%d", number); // We can not use this method in c89 */ 204 | sprintf(big_integer, "%d", (int)number); 205 | lua_pushstring(L, big_integer); 206 | lua_pushvalue(L, lua_upvalueindex(SSI_SEMICOLON)); 207 | } 208 | } 209 | break; 210 | 211 | case LUA_TSTRING: 212 | { 213 | size_t len = 0; 214 | const char * str = lua_tolstring(L, index, &len); 215 | if (Plookslikeinteger(str, len) == 0) 216 | { 217 | lua_pushnil(L); 218 | lua_pushfstring(L, "string keys convertible to integers are not supported"); 219 | error = 1; 220 | } 221 | else 222 | { 223 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_BEGIN)); 224 | lua_pushinteger(L, len); 225 | lua_tostring(L, -1); 226 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_MIDDLE)); 227 | lua_pushvalue(L, index); /* Note that the string need not to be escaped. */ 228 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_END)); 229 | } 230 | } 231 | break; 232 | 233 | case LUA_TNIL: 234 | case LUA_TNONE: 235 | case LUA_TBOOLEAN: 236 | case LUA_TTABLE: 237 | case LUA_TFUNCTION: 238 | case LUA_TUSERDATA: 239 | case LUA_TTHREAD: 240 | case LUA_TLIGHTUSERDATA: 241 | default: 242 | lua_pushnil(L); 243 | lua_pushfstring(L, "unsupported key type `%s'", lua_typename(L, type)); 244 | error = 1; 245 | break; 246 | } 247 | 248 | return error; 249 | } 250 | 251 | /* 252 | * Warning: Index must be absolute! For example, you must use lua_gettop() instead of -1. 253 | * Note: Upvalues referenced here are belong to Cphpserialize function below. 254 | */ 255 | static int Pphpserializevalue(lua_State * L, int index, int php_base_index, int nesting) 256 | { 257 | int type = LUA_TNONE; 258 | int error = 0; 259 | 260 | LCALL(L, stack); 261 | 262 | do 263 | { 264 | if (nesting > PHPSERIALIZE_MAXNESTING) 265 | { 266 | lua_pushnil(L); 267 | lua_pushliteral(L, "too deep"); 268 | error = 1; 269 | break; 270 | } 271 | 272 | luaL_checkstack(L, 10, "serializevalue"); /* Arbitrary value */ 273 | 274 | type = lua_type(L, index); 275 | switch (type) 276 | { 277 | case LUA_TNIL: 278 | case LUA_TNONE: 279 | lua_pushvalue(L, lua_upvalueindex(SSI_NULL)); 280 | break; 281 | 282 | case LUA_TNUMBER: 283 | { 284 | char big_integer[256] = {0}; 285 | /* TODO: Ensure this is correct check for number being integer. Check corner cases (math.huge, NaN, epsilon, etc.)! */ 286 | lua_Number number = lua_tonumber(L, index); 287 | if (!is_float_finite(number) ) 288 | { 289 | lua_pushnil(L); 290 | lua_pushfstring(L, "NaN/Infinte values are not supported"); 291 | error = 1; 292 | } 293 | if (error == 0) 294 | { 295 | /* instead of using Lua`s tostring we make conversion by hand. This will overcome 10000... -> 1e14 issue */ 296 | /* was: lua_pushinteger(L, number);lua_tostring(L, -1);*/ 297 | if (number != (lua_Integer)number) 298 | { 299 | /*snprintf(big_integer, sizeof(big_integer), "%#." PHPSERIALIZE_FLOATPRECISION_STR "f", number); // We can not use this method in c89 */ 300 | sprintf(big_integer, "%#." PHPSERIALIZE_FLOATPRECISION_STR "f", number); 301 | lua_pushvalue(L, lua_upvalueindex(SSI_DOUBLE)); 302 | } 303 | else 304 | { 305 | lua_Integer number_int = (lua_Integer)lua_tonumber(L, index); 306 | /*snprintf(big_integer, sizeof(big_integer), "%d", number_int); // We can not use this method in c89 */ 307 | sprintf(big_integer, "%d", (int)number_int); 308 | lua_pushvalue(L, lua_upvalueindex(SSI_INTEGER)); 309 | } 310 | lua_pushstring(L, big_integer); 311 | lua_pushvalue(L, lua_upvalueindex(SSI_SEMICOLON)); 312 | } 313 | } 314 | break; 315 | 316 | case LUA_TBOOLEAN: 317 | lua_pushvalue(L, lua_upvalueindex((lua_toboolean(L, index) == 1) ? SSI_BOOL_TRUE : SSI_BOOL_FALSE)); 318 | break; 319 | 320 | case LUA_TSTRING: 321 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_BEGIN)); 322 | lua_pushinteger(L, lua_objlen(L, index)); 323 | lua_tostring(L, -1); 324 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_MIDDLE)); 325 | lua_pushvalue(L, index); /* Note that the string need not to be escaped. */ 326 | lua_pushvalue(L, lua_upvalueindex(SSI_STRING_END)); 327 | break; 328 | 329 | case LUA_TTABLE: 330 | { 331 | int size = 0; 332 | int sizePos = 0; 333 | int base_table_pos = lua_gettop(L); 334 | lua_pushvalue(L, lua_upvalueindex(SSI_ARRAY_BEGIN)); 335 | lua_pushnil(L); /* Size placeholder */ 336 | sizePos = lua_gettop(L); 337 | lua_pushvalue(L, lua_upvalueindex(SSI_ARRAY_MIDDLE)); 338 | 339 | lua_pushnil(L); /* First key */ 340 | while (error == 0 && lua_next(L, index) != 0) 341 | { 342 | int valuePos = lua_gettop(L); 343 | int keyPos = valuePos - 1; 344 | error = Pphpserializekey(L, keyPos, php_base_index); 345 | 346 | if (error == 0) 347 | { 348 | error = Pphpserializevalue(L, valuePos, php_base_index, nesting + 1); 349 | lua_remove(L, valuePos); 350 | } 351 | 352 | if (error == 0) 353 | { 354 | ++size; 355 | 356 | /* Move key to front of stack */ 357 | lua_pushvalue(L, keyPos); 358 | lua_remove(L, keyPos); 359 | } 360 | } 361 | 362 | if (error == 0) 363 | { 364 | lua_pushvalue(L, lua_upvalueindex(SSI_ARRAY_END)); 365 | lua_pushinteger(L, size); 366 | lua_tostring(L, -1); 367 | lua_replace(L, sizePos); 368 | 369 | maybe_concat(L, base_table_pos + 1, 1); 370 | } 371 | } 372 | break; 373 | 374 | case LUA_TFUNCTION: 375 | case LUA_TUSERDATA: 376 | case LUA_TTHREAD: 377 | case LUA_TLIGHTUSERDATA: 378 | default: 379 | lua_pushnil(L); 380 | lua_pushfstring(L, "unsupported value type `%s'", lua_typename(L, type)); 381 | error = 1; 382 | break; 383 | } 384 | } while (0); 385 | 386 | if (error != 0) 387 | { 388 | if (LEXTRA(L, stack) < 2) 389 | { 390 | return luaL_error(L, "implementation error: missing error message"); 391 | } 392 | 393 | luaL_checkstack(L, 2, "serializevalue"); 394 | 395 | /* Clean up stack, leaving only nil and error message */ 396 | lua_insert(L, LBASE(L, stack) + 1); 397 | lua_insert(L, LBASE(L, stack) + 1); 398 | 399 | lua_pop(L, LEXTRA(L, stack) - 2); 400 | } 401 | 402 | return error; 403 | } 404 | 405 | static int Cphpserialize(lua_State * L) 406 | { 407 | int success = 0; 408 | LCALL(L, stack); 409 | 410 | int php_base_index = luaL_optint(L, 2, 1); 411 | success = (Pphpserializevalue(L, 1, php_base_index, 0) == 0); 412 | 413 | if (!success) 414 | { 415 | LRET(L, stack, 2); /* Nil with error message */ 416 | } 417 | 418 | /* Warning: This would attempt to concat *all* values added on stack since this function called. 419 | * Beware of stack balancing errors! Ensure only string values are left in stack. 420 | */ 421 | lua_concat(L, LEXTRA(L, stack)); 422 | 423 | LRET(L, stack, 1); 424 | } 425 | 426 | /* 427 | * Usage example: 428 | * 429 | * 430 | * TODO: 431 | * 432 | * -- phpunserialize 433 | * -- var_export-like serialization (to PHP code, not to unserializeable string) 434 | * -- maximum tolerance version, which attempts to serialize as much as ever possible, 435 | * converts non-integer numeric keys to strings, integer string keys to integers etc. 436 | * 437 | * -- NaN, math.huge, epsilon tests 438 | * -- Self save-load cycle tests 439 | * -- os.exec("php -r") tests (both by load-unload and by serialized string comparsion) 440 | * -- also look at php own serialize/unserialize tests, and borrow test data from there. 441 | * -- test large (>BUFSIZ) amounts of data (both as solid strings and as a lot of small objects) 442 | * -- fit code in 80 chars line width 443 | * -- add support for table recusrion/references (R:...) 444 | * -- Use own auto-growing char buffer instead of Lua stack on save. 445 | * 446 | * -- Remove debugging stuff from public version 447 | * -- Collapse data in stack if its size is close to LUAI_MAXCSTACK 448 | * -- In unserialize() validate string/table sizes (see luabins) 449 | * 450 | */ 451 | 452 | #ifdef __cplusplus 453 | extern "C" 454 | { 455 | #endif 456 | 457 | LUALIB_API int luaopen_phpserialize(lua_State * L) 458 | { 459 | int i = 0; 460 | 461 | LCALL(L, stack); 462 | 463 | lua_newtable(L); 464 | 465 | luaL_checkstack(L, NUM_SSI, "luaopen_phpserialize"); 466 | for (i = 1; i <= NUM_SSI; ++i) /* Note string array is one-based */ 467 | { 468 | lua_pushstring(L, g_SerializeStrings[i]); 469 | } 470 | 471 | lua_pushcclosure(L, Cphpserialize, NUM_SSI); 472 | lua_setfield(L, -2, "phpserialize"); 473 | 474 | LRET(L, stack, 1); 475 | } 476 | 477 | #ifdef __cplusplus 478 | } 479 | #endif 480 | -------------------------------------------------------------------------------- /src/lua-phpserialize.def: -------------------------------------------------------------------------------- 1 | LIBRARY lua-phpserialize.dll 2 | DESCRIPTION "lua-phpserialize" 3 | VERSION 0.0.1 4 | EXPORTS 5 | luaopen_phpserialize 6 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * util.c: Utility functions. 3 | * This file is a part of lua-phpserialize library. 4 | * Copyright (c) lua-phpserialize authors (see file `COPYRIGHT` for the license) 5 | */ 6 | 7 | #include "lua.h" 8 | #include "lauxlib.h" 9 | #include "util.h" 10 | 11 | int Pdumpstack(lua_State * L, int base) 12 | { 13 | int top = lua_gettop(L); 14 | 15 | if (top == 0) 16 | { 17 | lua_pushliteral(L, "-- stack is empty --"); 18 | } 19 | else 20 | { 21 | int pos = 0; 22 | int have_tostring = 0; 23 | luaL_Buffer b; 24 | 25 | lua_getglobal(L, "tostring"); 26 | have_tostring = lua_isfunction(L, -1); 27 | 28 | luaL_buffinit(L, &b); 29 | 30 | for (pos = top; pos > 0; --pos) 31 | { 32 | if (pos == base) 33 | { 34 | luaL_addstring(&b, "-- base --\n"); 35 | } 36 | luaL_addstring(&b, "["); 37 | lua_pushinteger(L, pos); 38 | luaL_addvalue(&b); 39 | luaL_addstring(&b, "] - "); 40 | luaL_addstring(&b, luaL_typename(L, pos)); 41 | luaL_addstring(&b, " - `"); 42 | 43 | /* Call lua's tostring on value */ 44 | if (have_tostring) 45 | { 46 | lua_pushvalue(L, top + 1); 47 | lua_pushvalue(L, pos); 48 | lua_call(L, 1, 1); 49 | } 50 | else 51 | { 52 | lua_pushvalue(L, pos); 53 | lua_tostring(L, -1); 54 | } 55 | 56 | luaL_addvalue(&b); 57 | luaL_addstring(&b, "'\n"); 58 | } 59 | 60 | luaL_pushresult(&b); 61 | lua_remove(L, -2); /* Pop tostring() */ 62 | if (lua_gettop(L) != top + 1) 63 | { 64 | /* Note: Can't use macros here, since macros use this function */ 65 | return luaL_error(L, "dumpstack not balanced (1)"); 66 | } 67 | } 68 | 69 | lua_getglobal(L, "print"); 70 | if (!lua_isfunction(L, -1)) 71 | { 72 | lua_pop(L, 1); 73 | printf("%s", lua_tostring(L, -1)); 74 | lua_pop(L, 1); 75 | } 76 | 77 | /* Move string to top of stack */ 78 | lua_pushvalue(L, -2); 79 | lua_remove(L, -3); 80 | 81 | lua_call(L, 1, 0); 82 | 83 | if (lua_gettop(L) != top) 84 | { 85 | /* Note: Can't use macros here, since macros use this function */ 86 | return luaL_error(L, "dumpstack not balanced (2)"); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * util.h: Utility functions. 3 | * This file is a part of lua-phpserialize library. 4 | * Copyright (c) lua-phpserialize authors (see file `COPYRIGHT` for the license) 5 | */ 6 | 7 | #ifndef UTIL_H_ 8 | #define UTIL_H_ 9 | 10 | #if defined(__WIN32__) || defined(WIN32) || defined(_WIN32) 11 | # define PLATFORM_WIN32 12 | #else 13 | # define PLATFORM_LINUX 14 | # if defined(__APPLE__) 15 | # define PLATFORM_OSX 16 | # endif 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | #if defined(PLATFORM_LINUX) 23 | 24 | /* There is no finit in c89, so we using this define ti fix c89-conformance warning and isfinit*/ 25 | #ifndef __USE_MISC 26 | #define __USE_MISC 27 | #endif 28 | #include 29 | #define is_float_finite finite 30 | 31 | #elif defined(PLATFORM_WIN32) 32 | 33 | #include 34 | #define is_float_finite _finite 35 | #define snprintf _snprintf 36 | 37 | #endif 38 | 39 | int Pdumpstack(lua_State * L, int base); 40 | 41 | #define LCALL(L, v) int v = lua_gettop(L); 42 | 43 | #define LCHECK(L, v, n) \ 44 | if (lua_gettop(L) != (v) + (n)) \ 45 | { \ 46 | Pdumpstack(L, (v)); \ 47 | return luaL_error( \ 48 | L, "%s(%d): unbalanced implementation (base: %d, top: %d, expected %d)", \ 49 | __FILE__, __LINE__, (v), lua_gettop(L), (v) + (n) \ 50 | ); \ 51 | } 52 | 53 | #define LRET(L, v, n) LCHECK(L, v, n); return (n); 54 | #define LBASE(L, v) (v) 55 | #define LTOP(L, v) (lua_gettop(L)) 56 | #define LEXTRA(L, v) (LTOP(L, v) - LBASE(L, v)) 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /test/test.lua: -------------------------------------------------------------------------------- 1 | -- test.lua: lua-phpserialize module tests 2 | -- This file is a part of lua-phpserialize library. 3 | -- Copyright (c) lua-phpserialize authors (see file `COPYRIGHT` for the license) 4 | 5 | -- TODO: This test fails under LuaJIT2 due to different table traverse order. 6 | -- However, lua-phpserialize does support LJ2 7 | 8 | local check_phpserialize = function(t, actual, expected) 9 | if actual ~= expected then 10 | print("Phpserialize test failed: ".. t ..".\nactual:\n'" .. tostring(actual) .. "'\nexpected:\n'" .. tostring(expected) .. "'\n") 11 | end 12 | end 13 | 14 | local check_phpserialize_match = function(t, actual, expected) 15 | if not actual:match(expected) then 16 | error("Phpserialize test_match failed: ".. t ..".\nactual:\n'" .. tostring(actual) .. "'\nexpected:\n'" .. tostring(expected) .. "'\n") 17 | end 18 | end 19 | 20 | local phpserialize = require "phpserialize" 21 | 22 | -- Test that default array offset is 1 23 | local test_serialize = function() 24 | do 25 | local luatable = { "one" } 26 | local phpsrl = phpserialize.phpserialize(luatable); 27 | local expected = [=[a:1:{i:1;s:3:"one";}]=] 28 | check_phpserialize("step1",phpsrl,expected) 29 | end 30 | 31 | do 32 | local luatable = {} 33 | luatable['int1'] = 10 34 | luatable['int2'] = -10 35 | luatable['bool1'] = true 36 | luatable['bool2'] = false 37 | luatable['nil1'] = nil 38 | 39 | local phpsrl = phpserialize.phpserialize(luatable,1); 40 | local expected = [=[a:4:{s:4:"int2";i:-10;s:4:"int1";i:10;s:5:"bool2";b:0;s:5:"bool1";b:1;}]=] 41 | check_phpserialize("step2",phpsrl,expected) 42 | end 43 | 44 | do 45 | local luatable = {} 46 | luatable['float3'] = 10.00000000000001 47 | local phpsrl = phpserialize.phpserialize(luatable,1); 48 | local expected = [=[a:1:%{s:6:"float3";d:10%.00000000000001.-;%}]=] 49 | check_phpserialize_match("step2-1",phpsrl,expected) 50 | end 51 | 52 | do 53 | local luatable={} 54 | local phpsrl = phpserialize.phpserialize(luatable,3); 55 | local expected = [=[a:0:{}]=] 56 | check_phpserialize("step3",phpsrl,expected) 57 | end 58 | 59 | do 60 | local luatable = {} 61 | luatable[#luatable+1] = 1 62 | luatable[#luatable+1] = {} 63 | local phpsrl = phpserialize.phpserialize(luatable,0); 64 | local expected = [=[a:2:{i:0;i:1;i:1;a:0:{}}]=] 65 | check_phpserialize("step4",phpsrl,expected) 66 | end 67 | 68 | do 69 | local luatable = {} 70 | luatable["asd"] = 0/0 71 | local phpsrl = phpserialize.phpserialize(luatable,1); 72 | local expected = nil 73 | check_phpserialize("step5",phpsrl,expected) 74 | end 75 | 76 | do 77 | local luatable={} 78 | luatable[#luatable+1] = "asdasd" 79 | luatable[#luatable+1] = 100 80 | luatable[#luatable+1] = -123 81 | local phpsrl = phpserialize.phpserialize(luatable,3); 82 | local expected = [=[a:3:{i:3;s:6:"asdasd";i:4;i:100;i:5;i:-123;}]=] 83 | check_phpserialize("step6",phpsrl,expected) 84 | end 85 | 86 | do 87 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 88 | local phpsrl = phpserialize.phpserialize(luatable,1); 89 | local expected = [=[a:3:{s:6:"qweqwe";i:-5;s:3:"dfb";s:1:"2";s:6:"asdasd";i:1;}]=] 90 | check_phpserialize("step7",phpsrl,expected) 91 | end 92 | 93 | do 94 | local luatable = {} 95 | luatable[ [=[aa"aa]=] ] = "aaa" 96 | luatable[ [=[bb\'bb]=] ] = "bbb" 97 | luatable[ [=[cc'cc]=] ] = "ccc" 98 | local phpsrl = phpserialize.phpserialize(luatable,1); 99 | local expected = [=[a:3:{s:5:"aa"aa";s:3:"aaa";s:6:"bb\'bb";s:3:"bbb";s:5:"cc'cc";s:3:"ccc";}]=] 100 | check_phpserialize("step8",phpsrl,expected) 101 | end 102 | 103 | do 104 | local luatable = {} 105 | luatable[ "aaa" ] = [=[aa"aa]=] 106 | luatable[ "bbb" ] = [=[bb\'bb]=] 107 | luatable[ "ccc" ] = [=[cc'cc]=] 108 | local phpsrl = phpserialize.phpserialize(luatable,1); 109 | local expected = [=[a:3:{s:3:"ccc";s:5:"cc'cc";s:3:"bbb";s:6:"bb\'bb";s:3:"aaa";s:5:"aa"aa";}]=] 110 | check_phpserialize("step9",phpsrl,expected) 111 | end 112 | 113 | do 114 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 115 | local luatable2 = {tab2a = "asdasd", tab2b = 15} 116 | luatable.recurse1 = {} 117 | luatable.recurse2 = luatable2 118 | luatable.recurse1.blablalba = luatable2 119 | local phpsrl = phpserialize.phpserialize(luatable,0); 120 | local expected = [=[a:5:{s:8:"recurse2";a:2:{s:5:"tab2a";s:6:"asdasd";s:5:"tab2b";i:15;}s:8:"recurse1";a:1:{s:9:"blablalba";a:2:{s:5:"tab2a";s:6:"asdasd";s:5:"tab2b";i:15;}}s:6:"qweqwe";i:-5;s:3:"dfb";s:1:"2";s:6:"asdasd";i:1;}]=] 121 | check_phpserialize("step10",phpsrl,expected) 122 | end 123 | 124 | do 125 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 126 | local luatable2 = {tab2a = "asdasd", tab2b = 15} 127 | luatable.recurse1 = {} 128 | luatable.recurse2 = luatable2 129 | luatable.recurse1.blablalba = luatable2 130 | local phpsrl = phpserialize.phpserialize(luatable,0); 131 | local expected = [=[a:5:{s:8:"recurse2";a:2:{s:5:"tab2a";s:6:"asdasd";s:5:"tab2b";i:15;}s:8:"recurse1";a:1:{s:9:"blablalba";a:2:{s:5:"tab2a";s:6:"asdasd";s:5:"tab2b";i:15;}}s:6:"qweqwe";i:-5;s:3:"dfb";s:1:"2";s:6:"asdasd";i:1;}]=] 132 | check_phpserialize("step11",phpsrl,expected) 133 | end 134 | 135 | do 136 | local luatable = { 137 | 15; 138 | "123123"; 139 | "sdfvxcv"; 140 | rec2={12;zxcc=1;}; 141 | rec3={xcxx="2wefrw";}; 142 | } 143 | local phpsrl = phpserialize.phpserialize(luatable,0); 144 | local expected = [=[a:5:{i:0;i:15;i:1;s:6:"123123";i:2;s:7:"sdfvxcv";s:4:"rec3";a:1:{s:4:"xcxx";s:6:"2wefrw";}s:4:"rec2";a:2:{i:0;i:12;s:4:"zxcc";i:1;}}]=] 145 | check_phpserialize("step12",phpsrl,expected) 146 | end 147 | 148 | do 149 | local luatable = {} 150 | luatable[#luatable+1] = "111V_sdfv\"'=%$#@!^&*(*)_+_=-=xcv" 151 | luatable["222K_sdfv\"'=%$#@!^&*(*)_+_=-=xcv_KEYS2"] = "222VVVV_sdfv\"'=%$#@!^&*(*)_+_=-=xcv2" 152 | local phpsrl = phpserialize.phpserialize(luatable,0); 153 | local expected = [=[a:2:{i:0;s:32:"111V_sdfv"'=%$#@!^&*(*)_+_=-=xcv";s:38:"222K_sdfv"'=%$#@!^&*(*)_+_=-=xcv_KEYS2";s:36:"222VVVV_sdfv"'=%$#@!^&*(*)_+_=-=xcv2";}]=] 154 | check_phpserialize("step13",phpsrl,expected) 155 | end 156 | 157 | do 158 | local luatable = {} 159 | luatable[#luatable+1] = "val1" 160 | --luatable[12.1] = "val2-a" 161 | luatable[ 2110000000] = "val2-a" 162 | luatable[-2110000000] = "val2-b" 163 | luatable[-100] = "sdfsdfsdf" 164 | local phpsrl = phpserialize.phpserialize(luatable,1); 165 | local expected = [=[a:4:{i:1;s:4:"val1";i:-2110000000;s:6:"val2-b";i:2110000000;s:6:"val2-a";i:-100;s:9:"sdfsdfsdf";}]=] 166 | check_phpserialize("step14",phpsrl,expected) 167 | end 168 | end 169 | 170 | -- fail patterns 171 | local test_serializefails = function() 172 | do 173 | local luatable = {} 174 | luatable[-10/0] = "10 on zero" 175 | local phpsrl = phpserialize.phpserialize(luatable,1); 176 | local expected = nil 177 | check_phpserialize("step15",phpsrl,expected) 178 | end 179 | 180 | do 181 | local luatable = {} 182 | luatable[10/0] = "10 on zero" 183 | local phpsrl = phpserialize.phpserialize(luatable,1); 184 | local expected = nil 185 | check_phpserialize("step16",phpsrl,expected) 186 | end 187 | 188 | do 189 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 190 | luatable[0.56] = "DrobnijFig" 191 | local phpsrl = phpserialize.phpserialize(luatable,1); 192 | local expected = nil 193 | check_phpserialize("step17",phpsrl,expected) 194 | end 195 | 196 | do 197 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 198 | luatable[true] = "???" 199 | local phpsrl = phpserialize.phpserialize(luatable,1); 200 | local expected = nil 201 | check_phpserialize("step18",phpsrl,expected) 202 | end 203 | 204 | do 205 | local luatable = {asdasd=1,dfb="2",qweqwe=-5;} 206 | luatable[{1}] = "???" 207 | local phpsrl = phpserialize.phpserialize(luatable,1); 208 | local expected = nil 209 | check_phpserialize("step19",phpsrl,expected) 210 | end 211 | 212 | do 213 | local luatable={asdasd=1,dfb="2",qweqwe=-5;} 214 | luatable.recurse1 = luatable 215 | local phpsrl = phpserialize.phpserialize(luatable,0); 216 | local expected = nil 217 | check_phpserialize("step20",phpsrl,expected) 218 | end 219 | 220 | do 221 | local luatable={asdasd=1,dfb="2",qweqwe=-5;} 222 | luatable.recurse1 = {} 223 | luatable.recurse1.blablalba = luatable 224 | local phpsrl = phpserialize.phpserialize(luatable,0); 225 | local expected = nil 226 | check_phpserialize("step21",phpsrl,expected) 227 | end 228 | 229 | do 230 | local luatable = {} 231 | luatable[12.1] = "val2-a" 232 | local phpsrl = phpserialize.phpserialize(luatable,1); 233 | local expected = nil 234 | check_phpserialize("step22",phpsrl,expected) 235 | end 236 | end 237 | 238 | 239 | local test_serializettbl = function() 240 | local serialize_result = function(tbl) 241 | return phpserialize.phpserialize(tbl,0) 242 | end 243 | local ensure_strequals = function(msg, serializedval, expectedval) 244 | check_phpserialize(msg, tostring(serializedval), tostring(expectedval)) 245 | end 246 | local recursive_nil = false 247 | 248 | -- copy-pasted from test_tshort, table.lua 249 | ensure_strequals("", serialize_result({}), "a:0:{}") 250 | ensure_strequals("", serialize_result({1}), "a:1:{i:0;i:1;}") 251 | ensure_strequals("", serialize_result({a = 1}), [=[a:1:{s:1:"a";i:1;}]=]) 252 | ensure_strequals("", serialize_result({a = 1, 1, 2, 3}), [=[a:4:{i:0;i:1;i:1;i:2;i:2;i:3;s:1:"a";i:1;}]=]) 253 | ensure_strequals("", serialize_result({a = {}, 1, {1}, 3}), [=[a:4:{i:0;i:1;i:1;a:1:{i:0;i:1;}i:2;i:3;s:1:"a";a:0:{}}]=]) 254 | ensure_strequals("", serialize_result({["1"]=2,[1]=3}), [=[nil]=]) 255 | ensure_strequals("", serialize_result({a=false,[1]=false}), [=[a:2:{s:1:"a";b:0;i:0;b:0;}]=]) 256 | ensure_strequals("", serialize_result(nil), [=[N;]=]) 257 | ensure_strequals("", serialize_result(2), [=[i:2;]=]) 258 | ensure_strequals("", serialize_result("a\""), [=[s:2:"a"";]=]) 259 | ensure_strequals("", serialize_result(false), [=[b:0;]=]) 260 | ensure_strequals("", serialize_result(true), [=[b:1;]=]) 261 | ensure_strequals("", serialize_result(function()end),nil) 262 | ensure_strequals("", serialize_result(coroutine.create(function()end)),nil) 263 | ensure_strequals("", serialize_result(newproxy()),nil) 264 | ensure_strequals("", serialize_result({2}), [=[a:1:{i:0;i:2;}]=]) 265 | ensure_strequals("", serialize_result({"a\""}), [=[a:1:{i:0;s:2:"a"";}]=]) 266 | ensure_strequals("", serialize_result({"a\n"}), [=[a:1:{i:0;s:2:"a 267 | ";}]=]) 268 | ensure_strequals("", serialize_result({false}), [=[a:1:{i:0;b:0;}]=]) 269 | ensure_strequals("", serialize_result({true}), [=[a:1:{i:0;b:1;}]=]) 270 | ensure_strequals("", serialize_result({function()end}),nil) 271 | ensure_strequals("", serialize_result({coroutine.create(function()end)}),nil) 272 | ensure_strequals("", serialize_result({newproxy()}),nil) 273 | ensure_strequals("", serialize_result({[2] = true}), [=[a:1:{i:1;b:1;}]=]) 274 | ensure_strequals("", serialize_result({["a\""] = true}), [=[a:1:{s:2:"a"";b:1;}]=]) 275 | ensure_strequals("", serialize_result({["a\n"] = true}), [=[a:1:{s:2:"a 276 | ";b:1;}]=]) 277 | ensure_strequals("", serialize_result({[false] = true}), nil) 278 | ensure_strequals("", serialize_result({[true] = true}), nil) 279 | ensure_strequals("", serialize_result({[function()end] = true}),nil) 280 | ensure_strequals("", serialize_result({[coroutine.create(function()end)] = true}),nil) 281 | ensure_strequals("", serialize_result({[newproxy()] = true}),nil) 282 | ensure_strequals("", serialize_result({[0] = 2, [1] = 3, [2] = 4}), [=[a:3:{i:-1;i:2;i:1;i:4;i:0;i:3;}]=]) 283 | ensure_strequals("", serialize_result({[-1] = 2, [1] = 3, [2] = 4}), [=[a:3:{i:0;i:3;i:1;i:4;i:-2;i:2;}]=]) 284 | ensure_strequals("", serialize_result({[1] = 1, [2] = 2, [4] = 3}), [=[a:3:{i:0;i:1;i:1;i:2;i:3;i:3;}]=]) 285 | ensure_strequals("", serialize_result({[1] = 1, [2] = 2, [1024] = 3}), [=[a:3:{i:0;i:1;i:1;i:2;i:1023;i:3;}]=]) 286 | ensure_strequals("", serialize_result({[0.001] = 1}), nil) 287 | ensure_strequals("", serialize_result({[0.5] = 5, [1] = 10, [2] = 20}), nil) 288 | ensure_strequals("", serialize_result({[1] = 10, [1.5] = 15, [2] = 20}), nil) 289 | ensure_strequals("", serialize_result({[1] = 10, [2] = 20, [2.5] = 25}), nil) 290 | ensure_strequals("", serialize_result({nil, 7, nil, 18, nil, 64, nil, nil}), [=[a:3:{i:1;i:7;i:3;i:18;i:5;i:64;}]=]) 291 | ensure_strequals("", serialize_result({a=nil, b=7, c=nil, 38, nil}), [=[a:2:{i:0;i:38;s:1:"b";i:7;}]=]) 292 | ensure_strequals("", serialize_result({[{}]=7}), nil) 293 | ensure_strequals("", serialize_result({[{}]=true}), nil) 294 | ensure_strequals("plain", serialize_result({{1},[{1}]=true}), nil) 295 | local t = {1} 296 | if not recursive_nil then 297 | ensure_strequals("n1", serialize_result({t, t}), [=[a:2:{i:0;a:1:{i:0;i:1;}i:1;a:1:{i:0;i:1;}}]=]) 298 | ensure_strequals("nested", serialize_result({[t] = true, t}), nil) 299 | ensure_strequals("n2", serialize_result({[t]=t}), nil) 300 | else 301 | ensure_strequals("n1-t", serialize_result({t, t}), [=[]=]) 302 | ensure_strequals("nested-t", serialize_result({[t] = true, t}), [=[]=]) 303 | ensure_strequals("n2-t", serialize_result({[t]=t}), [=[]=]) 304 | end 305 | 306 | local r = {1} 307 | r[2] = r 308 | r.n = { r = r } 309 | if not recursive_nil then 310 | ensure_strequals("r1", serialize_result(r), nil) -- '{1,"table (recursive)",n={r="table (recursive)"}}') 311 | else 312 | ensure_strequals("r1-t", serialize_result(r), [=[]=]) 313 | end 314 | 315 | local mt = 316 | { 317 | __index = function(t, k) 318 | rawset(t, k, k) 319 | return k 320 | end; 321 | } 322 | ensure_strequals("mt1", serialize_result(setmetatable({}, mt)), [=[a:0:{}]=]) -- Say no to infinite loops! 323 | end 324 | 325 | local test_stackoverflow = function() 326 | local t = { 327 | {title="some long title",id=1,kind=0,rows={ 328 | {value=3404,user_gender=2,user_nick="01234567890",user_id=6922}, 329 | {value=3185,user_gender=2,user_nick="01234567890",user_id=2778}, 330 | {value=2636,user_gender=1,user_nick="01234567890",user_id=12004}, 331 | {value=2551,user_gender=1,user_nick="01234567890",user_id=11013}, 332 | {value=2323,user_gender=1,user_nick="01234567890",user_id=20891}, 333 | {value=2263,user_gender=1,user_nick="01234567890",user_id=2037}, 334 | {value=1850,user_gender=2,user_nick="01234567890",user_id=9827}, 335 | {value=1698,user_gender=2,user_nick="01234567890",user_id=20160}, 336 | {value=1696,user_gender=1,user_nick="01234567890",user_id=1186}, 337 | {value=1588,user_gender=1,user_nick="01234567890",user_id=105500}, 338 | {value=1569,user_gender=2,user_nick="01234567890",user_id=117509}, 339 | {value=1543,user_gender=1,user_nick="01234567890",user_id=13315}, 340 | {value=1465,user_gender=1,user_nick="01234567890",user_id=12756}, 341 | {value=1446,user_gender=2,user_nick="01234567890",user_id=3195}, 342 | {value=1438,user_gender=1,user_nick="01234567890",user_id=368442}, 343 | {value=1423,user_gender=2,user_nick="01234567890",user_id=7468}, 344 | {value=1409,user_gender=1,user_nick="01234567890",user_id=269852}, 345 | {value=1406,user_gender=1,user_nick="01234567890",user_id=3007}, 346 | {value=1399,user_gender=2,user_nick="01234567890",user_id=12051}, 347 | {value=1391,user_gender=1,user_nick="01234567890",user_id=23909}, 348 | {value=1368,user_gender=1,user_nick="01234567890",user_id=10846}, 349 | {value=1352,user_gender=1,user_nick="01234567890",user_id=140900}, 350 | {value=1263,user_gender=2,user_nick="01234567890",user_id=36668}, 351 | {value=1256,user_gender=2,user_nick="01234567890",user_id=32}, 352 | {value=1228,user_gender=2,user_nick="01234567890",user_id=29901}, 353 | {value=1222,user_gender=2,user_nick="01234567890",user_id=37142}, 354 | {value=1213,user_gender=2,user_nick="01234567890",user_id=31}, 355 | {value=1200,user_gender=2,user_nick="01234567890",user_id=51668}, 356 | {value=1198,user_gender=1,user_nick="01234567890",user_id=27347}, 357 | {value=1185,user_gender=1,user_nick="01234567890",user_id=18299}, 358 | {value=1180,user_gender=1,user_nick="01234567890",user_id=235980}, 359 | {value=1177,user_gender=1,user_nick="01234567890",user_id=120193}, 360 | {value=1168,user_gender=2,user_nick="01234567890",user_id=11251}, 361 | {value=1163,user_gender=1,user_nick="01234567890",user_id=445691}, 362 | {value=1161,user_gender=2,user_nick="01234567890",user_id=39336}, 363 | {value=1144,user_gender=1,user_nick="01234567890",user_id=324648}, 364 | {value=1139,user_gender=1,user_nick="01234567890",user_id=2617}, 365 | {value=1137,user_gender=2,user_nick="01234567890",user_id=7314}, 366 | {value=1125,user_gender=1,user_nick="01234567890",user_id=263948}, 367 | {value=1120,user_gender=1,user_nick="01234567890",user_id=461572}, 368 | {value=1119,user_gender=1,user_nick="01234567890",user_id=6361}, 369 | {value=1110,user_gender=2,user_nick="01234567890",user_id=43337}, 370 | {value=1109,user_gender=1,user_nick="01234567890",user_id=196229}, 371 | {value=1106,user_gender=1,user_nick="01234567890",user_id=22973}, 372 | {value=1100,user_gender=2,user_nick="01234567890",user_id=11946}, 373 | {value=1098,user_gender=1,user_nick="01234567890",user_id=31553}, 374 | {value=1096,user_gender=1,user_nick="01234567890",user_id=286184}, 375 | {value=1094,user_gender=1,user_nick="01234567890",user_id=286704}, 376 | {value=1094,user_gender=1,user_nick="01234567890",user_id=39}, 377 | {value=1086,user_gender=1,user_nick="01234567890",user_id=31120}}, 378 | descrption="some long description" 379 | }, 380 | {title="some long title2",id=2,kind=0,rows={ 381 | {value=879156,user_gender=2,user_nick="01234567890",user_id=6922}, 382 | {value=870840,user_gender=1,user_nick="01234567890",user_id=1227}, 383 | {value=834413,user_gender=2,user_nick="01234567890",user_id=2778}, 384 | {value=779940,user_gender=1,user_nick="01234567890",user_id=11013}, 385 | {value=771886,user_gender=1,user_nick="01234567890",user_id=12004}, 386 | {value=696306,user_gender=1,user_nick="01234567890",user_id=11044}, 387 | {value=552185,user_gender=1,user_nick="01234567890",user_id=8886}, 388 | {value=540953,user_gender=1,user_nick="01234567890",user_id=2617}, 389 | {value=503989,user_gender=2,user_nick="01234567890",user_id=20160}, 390 | {value=503523,user_gender=1,user_nick="01234567890",user_id=286184}, 391 | {value=500319,user_gender=1,user_nick="01234567890",user_id=89}, 392 | {value=467098,user_gender=1,user_nick="01234567890",user_id=1186}, 393 | {value=453344,user_gender=1,user_nick="01234567890",user_id=377}, 394 | {value=447110,user_gender=2,user_nick="01234567890",user_id=27223}, 395 | {value=430384,user_gender=2,user_nick="01234567890",user_id=11054}, 396 | {value=423897,user_gender=1,user_nick="01234567890",user_id=244154}, 397 | {value=417563,user_gender=2,user_nick="01234567890",user_id=1688}, 398 | {value=410874,user_gender=2,user_nick="01234567890",user_id=7314}, 399 | {value=401740,user_gender=1,user_nick="01234567890",user_id=39}, 400 | {value=401085,user_gender=1,user_nick="01234567890",user_id=20891}, 401 | {value=391896,user_gender=2,user_nick="01234567890",user_id=117509}, 402 | {value=379260,user_gender=2,user_nick="01234567890",user_id=32}, 403 | {value=366247,user_gender=2,user_nick="01234567890",user_id=58748}, 404 | {value=363655,user_gender=1,user_nick="01234567890",user_id=25591}, 405 | {value=357264,user_gender=1,user_nick="01234567890",user_id=269852}, 406 | {value=347827,user_gender=2,user_nick="01234567890",user_id=9827}, 407 | {value=347399,user_gender=1,user_nick="01234567890",user_id=6038}, 408 | {value=346551,user_gender=2,user_nick="01234567890",user_id=8664}, 409 | {value=341588,user_gender=2,user_nick="01234567890",user_id=12270}, 410 | {value=321805,user_gender=2,user_nick="01234567890",user_id=3195}, 411 | {value=319622,user_gender=2,user_nick="01234567890",user_id=274749}, 412 | {value=317440,user_gender=2,user_nick="01234567890",user_id=6013}, 413 | {value=311992,user_gender=2,user_nick="01234567890",user_id=167355}, 414 | {value=310080,user_gender=2,user_nick="01234567890",user_id=360998}, 415 | {value=306893,user_gender=1,user_nick="01234567890",user_id=461572}, 416 | {value=305951,user_gender=1,user_nick="01234567890",user_id=140900}, 417 | {value=304156,user_gender=1,user_nick="01234567890",user_id=6361}, 418 | {value=299714,user_gender=2,user_nick="01234567890",user_id=12051}, 419 | {value=295021,user_gender=2,user_nick="01234567890",user_id=37142}, 420 | {value=290926,user_gender=1,user_nick="01234567890",user_id=2037}, 421 | {value=287596,user_gender=2,user_nick="01234567890",user_id=306461}, 422 | {value=287480,user_gender=2,user_nick="01234567890",user_id=11855}, 423 | {value=286542,user_gender=2,user_nick="01234567890",user_id=31}, 424 | {value=286281,user_gender=2,user_nick="01234567890",user_id=29901}, 425 | {value=285217,user_gender=2,user_nick="01234567890",user_id=8181}, 426 | {value=284868,user_gender=1,user_nick="01234567890",user_id=10846}, 427 | {value=283742,user_gender=2,user_nick="01234567890",user_id=9117}, 428 | {value=283248,user_gender=2,user_nick="01234567890",user_id=11251}, 429 | {value=282526,user_gender=2,user_nick="01234567890",user_id=292031}, 430 | {value=275837,user_gender=1,user_nick="01234567890",user_id=2885}}, 431 | descrption="some long description2" 432 | }, 433 | {title="some long title3",id=3,kind=0,rows={ 434 | {value=452,user_gender=1,user_nick="01234567890",user_id=20891}, 435 | {value=280,user_gender=2,user_nick="01234567890",user_id=6922}, 436 | {value=277,user_gender=1,user_nick="01234567890",user_id=25591}, 437 | {value=249,user_gender=1,user_nick="01234567890",user_id=263648}, 438 | {value=227,user_gender=2,user_nick="01234567890",user_id=20160}, 439 | {value=218,user_gender=1,user_nick="01234567890",user_id=496618}, 440 | {value=214,user_gender=1,user_nick="01234567890",user_id=532323}, 441 | {value=197,user_gender=1,user_nick="01234567890",user_id=525419}, 442 | {value=195,user_gender=1,user_nick="01234567890",user_id=272666}, 443 | {value=194,user_gender=1,user_nick="01234567890",user_id=2617}, 444 | {value=172,user_gender=2,user_nick="01234567890",user_id=483682}, 445 | {value=159,user_gender=1,user_nick="01234567890",user_id=124369}, 446 | {value=156,user_gender=1,user_nick="01234567890",user_id=338203}, 447 | {value=155,user_gender=2,user_nick="01234567890",user_id=377667}, 448 | {value=147,user_gender=1,user_nick="01234567890",user_id=286184}, 449 | {value=127,user_gender=1,user_nick="01234567890",user_id=402266}, 450 | {value=122,user_gender=1,user_nick="01234567890",user_id=175083}, 451 | {value=122,user_gender=2,user_nick="01234567890",user_id=31697}, 452 | {value=118,user_gender=1,user_nick="01234567890",user_id=520979}, 453 | {value=112,user_gender=1,user_nick="01234567890",user_id=263948}, 454 | {value=104,user_gender=2,user_nick="01234567890",user_id=2778}, 455 | {value=103,user_gender=2,user_nick="01234567890",user_id=31}, 456 | {value=100,user_gender=1,user_nick="01234567890",user_id=611218}, 457 | {value=100,user_gender=1,user_nick="01234567890",user_id=385851}, 458 | {value=96,user_gender=2,user_nick="01234567890",user_id=299627}, 459 | {value=94,user_gender=1,user_nick="01234567890",user_id=478842}, 460 | {value=91,user_gender=1,user_nick="01234567890",user_id=550750}, 461 | {value=91,user_gender=2,user_nick="01234567890",user_id=506750}, 462 | {value=91,user_gender=1,user_nick="01234567890",user_id=39279}, 463 | {value=90,user_gender=1,user_nick="01234567890",user_id=269852}, 464 | {value=86,user_gender=1,user_nick="01234567890",user_id=244154}, 465 | {value=86,user_gender=2,user_nick="01234567890",user_id=117509}, 466 | {value=86,user_gender=1,user_nick="01234567890",user_id=2129}, 467 | {value=85,user_gender=2,user_nick="01234567890",user_id=439407}, 468 | {value=85,user_gender=2,user_nick="01234567890",user_id=29974}, 469 | {value=84,user_gender=1,user_nick="01234567890",user_id=286704}, 470 | {value=84,user_gender=1,user_nick="01234567890",user_id=10846}, 471 | {value=83,user_gender=1,user_nick="01234567890",user_id=519785}, 472 | {value=83,user_gender=1,user_nick="01234567890",user_id=8886}, 473 | {value=80,user_gender=2,user_nick="01234567890",user_id=39336}, 474 | {value=80,user_gender=1,user_nick="01234567890",user_id=6038}, 475 | {value=80,user_gender=1,user_nick="01234567890",user_id=5740}, 476 | {value=79,user_gender=2,user_nick="01234567890",user_id=368240}, 477 | {value=78,user_gender=2,user_nick="01234567890",user_id=282038}, 478 | {value=76,user_gender=1,user_nick="01234567890",user_id=355622}, 479 | {value=75,user_gender=1,user_nick="01234567890",user_id=296531}, 480 | {value=75,user_gender=1,user_nick="01234567890",user_id=12004}, 481 | {value=74,user_gender=1,user_nick="01234567890",user_id=511894}, 482 | {value=73,user_gender=1,user_nick="01234567890",user_id=349904}, 483 | {value=71,user_gender=1,user_nick="01234567890",user_id=579619}}, 484 | descrption="some long description3" 485 | }, 486 | {title="some long title4",id=4,kind=1,rows={ 487 | {value=986967,user_gender=2,user_nick="01234567890",user_id=2778}, 488 | {value=840776,user_gender=1,user_nick="01234567890",user_id=11013}, 489 | {value=825484,user_gender=1,user_nick="01234567890",user_id=12004}, 490 | {value=764637,user_gender=1,user_nick="01234567890",user_id=12756}, 491 | {value=747398,user_gender=1,user_nick="01234567890",user_id=2037}, 492 | {value=684397,user_gender=1,user_nick="01234567890",user_id=105500}, 493 | {value=680494,user_gender=2,user_nick="01234567890",user_id=167355}, 494 | {value=628037,user_gender=2,user_nick="01234567890",user_id=233774}, 495 | {value=623581,user_gender=1,user_nick="01234567890",user_id=196229}, 496 | {value=615290,user_gender=2,user_nick="01234567890",user_id=7468}, 497 | {value=590593,user_gender=1,user_nick="01234567890",user_id=140900}, 498 | {value=585232,user_gender=2,user_nick="01234567890",user_id=37142}, 499 | {value=577838,user_gender=1,user_nick="01234567890",user_id=9114}, 500 | {value=548899,user_gender=2,user_nick="01234567890",user_id=9827}, 501 | {value=541470,user_gender=2,user_nick="01234567890",user_id=237989}, 502 | {value=538520,user_gender=2,user_nick="01234567890",user_id=4065}, 503 | {value=536104,user_gender=2,user_nick="01234567890",user_id=107135}, 504 | {value=530338,user_gender=1,user_nick="01234567890",user_id=13315}, 505 | {value=528542,user_gender=2,user_nick="01234567890",user_id=6922}, 506 | {value=524878,user_gender=2,user_nick="01234567890",user_id=12051}, 507 | {value=520896,user_gender=1,user_nick="01234567890",user_id=235980}, 508 | {value=519491,user_gender=2,user_nick="01234567890",user_id=14259}, 509 | {value=514126,user_gender=2,user_nick="01234567890",user_id=117509}, 510 | {value=512395,user_gender=2,user_nick="01234567890",user_id=36668}, 511 | {value=510855,user_gender=2,user_nick="01234567890",user_id=39336}, 512 | {value=509947,user_gender=2,user_nick="01234567890",user_id=12889}, 513 | {value=508609,user_gender=1,user_nick="01234567890",user_id=25637}, 514 | {value=506813,user_gender=1,user_nick="01234567890",user_id=150611}, 515 | {value=505543,user_gender=1,user_nick="01234567890",user_id=3326}, 516 | {value=503774,user_gender=1,user_nick="01234567890",user_id=1186}, 517 | {value=500840,user_gender=2,user_nick="01234567890",user_id=8181}, 518 | {value=499252,user_gender=2,user_nick="01234567890",user_id=147897}, 519 | {value=493696,user_gender=2,user_nick="01234567890",user_id=29901}, 520 | {value=492974,user_gender=2,user_nick="01234567890",user_id=11946}, 521 | {value=492776,user_gender=2,user_nick="01234567890",user_id=7314}, 522 | {value=492453,user_gender=1,user_nick="01234567890",user_id=30619}, 523 | {value=489908,user_gender=1,user_nick="01234567890",user_id=6361}, 524 | {value=483260,user_gender=2,user_nick="01234567890",user_id=181605}, 525 | {value=479187,user_gender=2,user_nick="01234567890",user_id=408065}, 526 | {value=477070,user_gender=2,user_nick="01234567890",user_id=11251}, 527 | {value=476043,user_gender=2,user_nick="01234567890",user_id=383623}, 528 | {value=473512,user_gender=2,user_nick="01234567890",user_id=139383}, 529 | {value=472954,user_gender=1,user_nick="01234567890",user_id=296122}, 530 | {value=470538,user_gender=2,user_nick="01234567890",user_id=3195}, 531 | {value=468716,user_gender=2,user_nick="01234567890",user_id=20160}, 532 | {value=468223,user_gender=1,user_nick="01234567890",user_id=150641}, 533 | {value=467207,user_gender=2,user_nick="01234567890",user_id=267728}, 534 | {value=466187,user_gender=1,user_nick="01234567890",user_id=134043}, 535 | {value=466063,user_gender=1,user_nick="01234567890",user_id=286704}, 536 | {value=465972,user_gender=2,user_nick="01234567890",user_id=241855}}, 537 | descrption="some long description4" 538 | } 539 | } 540 | assert(phpserialize.phpserialize(t)) 541 | end 542 | 543 | 544 | test_serialize() 545 | test_serializefails() 546 | test_serializettbl() 547 | test_stackoverflow() 548 | 549 | print("OK") 550 | --------------------------------------------------------------------------------