JavaScript 版本的 PHP serialize/unserialize 完整实现

原创作品,禁止转载,欢迎链接。

以前写 PHPRPC 实现时,JavaScript 版本的序列化实现是修改自 http://www.devpro.it/code/102.html 的,这个实现虽然目前仍然在更新,不过它并没有完全实现 PHP 序列化的所有标记,因此它无法序列化复杂对象,例如嵌套对象,也无法反序列化所有的 PHP 序列化以后的内容。因此我重新编写了一个实现,这个实现与 PHP 5 的序列化完全兼容,并且可以反序列化 PHP 4、PHP 5 和 PHP 6 序列化的内容。支持魔术方法 __sleep 和 __wakeup,支持实现了 Serializable 接口的序列化和反序列化(在 JavaScript 没有接口的概念,因此只要对象中包含 serialize 和 unserialize 方法就可以了,关于 Serializable 接口与自定义序列化的更多内容请参见:PHP 序列化(serialize)格式详解——自定义对象序列化)。该实现兼容以下版本的浏览器(已测试):

  • IE 4+(包括 Windows Vista 的 IE 7+ 与 Windows Mobile 2003/Windows Moblie 5 的 IE 4)
  • Netscape 6+
  • Mozilla/Firefox
  • Opera(包括 Opera Mini 和 Opera Mobile,但对于 Opera Mobile 8.5 beta 2 for Windows Mobile 5 Pocket PC 对象序列化时,对象名的序列化支持的不完整,而其他版本都正常。)
  • Konqueror
  • Safari
  • Epiphany

其他浏览器未测试,但应该基本上只要支持 JavaScript 的浏览器都可以支持(有条件的用户希望能够帮忙测试)。

本程序中需要调用 utf.js 中的 utf16to8 和 utf8to16 这两个函数来进行字节流与 JavaScript 字符串之间的转换。 

JavaScript代码
 
  1. /**********************************************************\  
  2. |                                                          |  
  3. | The implementation of PHPRPC Protocol 3.0                |  
  4. |                                                          |  
  5. | phpserializer.js                                         |  
  6. |                                                          |  
  7. | Release 3.0.0 beta 10                                    |  
  8. | Copyright (c) 2005-2007 by Team-PHPRPC                   |  
  9. |                                                          |  
  10. | WebSite:  http://www.phprpc.org/                         |  
  11. |           http://www.phprpc.net/                         |  
  12. |           http://www.phprpc.com/                         |  
  13. |           http://sourceforge.net/projects/php-rpc/       |  
  14. |                                                          |  
  15. | Authors:  Ma Bingyao <andot@ujn.edu.cn>                  |  
  16. |                                                          |  
  17. | This file may be distributed and/or modified under the   |  
  18. | terms of the GNU Lesser General Public License (LGPL)    |  
  19. | version 3.0 as published by the Free Software Foundation |  
  20. | and appearing in the included file LICENSE.              |  
  21. |                                                          |  
  22. \**********************************************************/  
  23.   
  24. /* PHP serialize/unserialize library.  
  25.  *  
  26.  * Copyright (C) 2006-2007 Ma Bingyao <andot@ujn.edu.cn>  
  27.  * Version: 4.0  
  28.  * LastModified: Jun 27, 2007  
  29.  * This library is free.  You can redistribute it and/or modify it.  
  30.  */  
  31.   
  32. var PHPSerializer = (function () {   
  33.   
  34.     function freeEval(s) {   
  35.         return eval(s);   
  36.     }   
  37.   
  38.     return {   
  39.         serialize : function(o) {   
  40.             var p = 0, sb = [], ht = [], hv = 1;   
  41.             function getClassName(o) {   
  42.                 if (typeof(o) == 'undefined' || typeof(o.constructor) == 'undefined'return '';   
  43.                 var c = o.constructor.toString();   
  44.                 c = c.substr(0, c.indexOf('(')).replace(/(^\s*function\s*)|(\s*$)/ig, '').toUTF8();   
  45.                 return ((c == '') ? 'Object' : c);   
  46.             }   
  47.             function isInteger(n) {   
  48.                 var i, s = n.toString(), l = s.length;   
  49.                 if (l > 11) return false;   
  50.                 for (i = (s.charAt(0) == '-') ? 1 : 0; i < l; i++) {   
  51.                     switch (s.charAt(i)) {   
  52.                         case '0':   
  53.                         case '1':   
  54.                         case '2':   
  55.                         case '3':   
  56.                         case '4':   
  57.                         case '5':   
  58.                         case '6':   
  59.                         case '7':   
  60.                         case '8':   
  61.                         case '9'break;   
  62.                         default : return false;   
  63.                     }   
  64.                 }   
  65.                 return !(n < -2147483648 || n > 2147483647);   
  66.             }   
  67.             function inHashTable(o) {   
  68.                 var k;   
  69.                 for (k in ht) if (ht[k] === o) return k;   
  70.                 return false;   
  71.             }   
  72.             function serializeNull() {   
  73.                 sb[p++] = 'N;';   
  74.             }   
  75.             function serializeBoolean(b) {   
  76.                 sb[p++] = (b ? 'b:1;' : 'b:0;');   
  77.             }   
  78.             function serializeInteger(i) {   
  79.                 sb[p++] = 'i:' + i + ';';   
  80.             }   
  81.             function serializeDouble(d) {   
  82.                 if (isNaN(d)) d = 'NAN';   
  83.                 else if (d == Number.POSITIVE_INFINITY) d = 'INF';   
  84.                 else if (d == Number.NEGATIVE_INFINITY) d = '-INF';   
  85.                 sb[p++] = 'd:' + d + ';';   
  86.             }   
  87.             function serializeString(s) {   
  88.                 var utf8 = s.toUTF8();   
  89.                 sb[p++] = 's:' + utf8.length + ':"';   
  90.                 sb[p++] = utf8;   
  91.                 sb[p++] = '";';   
  92.             }   
  93.             function serializeDate(dt) {   
  94.                 sb[p++] = 'O:11:"PHPRPC_Date":7:{';   
  95.                 sb[p++] = 's:4:"year";';   
  96.                 serializeInteger(dt.getFullYear());   
  97.                 sb[p++] = 's:5:"month";';   
  98.                 serializeInteger(dt.getMonth() + 1);   
  99.                 sb[p++] = 's:3:"day";';   
  100.                 serializeInteger(dt.getDate());   
  101.                 sb[p++] = 's:4:"hour";';   
  102.                 serializeInteger(dt.getHours());   
  103.                 sb[p++] = 's:6:"minute";';   
  104.                 serializeInteger(dt.getMinutes());   
  105.                 sb[p++] = 's:6:"second";';   
  106.                 serializeInteger(dt.getSeconds());   
  107.                 sb[p++] = 's:11:"millisecond";';   
  108.                 serializeInteger(dt.getMilliseconds());   
  109.                 sb[p++] = '}';   
  110.             }   
  111.             function serializeArray(a) {   
  112.                 sb[p++] = 'a:';   
  113.                 var k, lp = p;   
  114.                 sb[p++] = 0;   
  115.                 sb[p++] = ':{';   
  116.                 for (k in a) {   
  117.                     if (typeof(a[k]) != 'function') {   
  118.                         isInteger(k) ? serializeInteger(k) : serializeString(k);   
  119.                         serialize(a[k]);   
  120.                         sb[lp]++;   
  121.                     }   
  122.                 }   
  123.                 sb[p++] = '}';   
  124.             }   
  125.             function serializeObject(o) {   
  126.                 var cn = getClassName(o);   
  127.                 if (cn == '') serializeNull();   
  128.                 else if (typeof(o.serialize) != 'function') {   
  129.                     sb[p++] = 'O:' + cn.length + ':"' + cn + '":';   
  130.                     var lp = p;   
  131.                     sb[p++] = 0;   
  132.                     sb[p++] = ':{';   
  133.                     var k;   
  134.                     if (typeof(o.__sleep) == 'function') {   
  135.                         var a = o.__sleep();   
  136.                         for (k in a) {   
  137.                             serializeString(a[k]);   
  138.                             serialize(o[a[k]]);   
  139.                             sb[lp]++;   
  140.                         }   
  141.                     }   
  142.                     else {   
  143.                         for (k in o) {   
  144.                             if (typeof(o[k]) != 'function') {   
  145.                                 serializeString(k);   
  146.                                 serialize(o[k]);   
  147.                                 sb[lp]++;   
  148.                             }   
  149.                         }   
  150.                     }   
  151.                     sb[p++] = '}';   
  152.                 }   
  153.                 else {   
  154.                     var cs = o.serialize();   
  155.                     sb[p++] = 'C:' + cn.length + ':"' + cn + '":' + cs.length + ':{' +cs + '}';   
  156.                 }   
  157.             }   
  158.             function serializePointRef(R) {   
  159.                 sb[p++] = 'R:' + R + ';';   
  160.             }   
  161.             function serializeRef(r) {   
  162.                 sb[p++] = 'r:' + r + ';';   
  163.             }   
  164.             function serialize(o) {   
  165.                 if (typeof(o) == "undefined" || o == null ||   
  166.                     o.constructor == Function) {   
  167.                     hv++;   
  168.                     serializeNull();   
  169.                     return;   
  170.                 }   
  171.                 var className = getClassName(o);   
  172.                 switch (o.constructor) {   
  173.                     case Boolean: {   
  174.                         hv++;   
  175.                         serializeBoolean(o);   
  176.                         break;   
  177.                     }   
  178.                     case Number: {   
  179.                         hv++;   
  180.                         isInteger(o) ? serializeInteger(o) : serializeDouble(o);   
  181.                         break;   
  182.                     }   
  183.                     case String: {   
  184.                         hv++;   
  185.                         serializeString(o);   
  186.                         break;   
  187.                     }   
  188.                     case Date: {   
  189.                         hv++;   
  190.                         serializeDate(o);   
  191.                         break;   
  192.                     }   
  193.                     default: {   
  194.                         if (className == "Object" || o.constructor == Array) {   
  195.                             var r = inHashTable(o);   
  196.                             if (r) {   
  197.                                 serializePointRef(r);   
  198.                             }   
  199.                             else {   
  200.                                 ht[hv++] = o;   
  201.                                 serializeArray(o);   
  202.                             }   
  203.                             break;   
  204.                         }   
  205.                         else {   
  206.                             var r = inHashTable(o);   
  207.                             if (r) {   
  208.                                 hv++;   
  209.                                 serializeRef(r);   
  210.                             }   
  211.                             else {   
  212.                                 ht[hv++] = o;   
  213.                                 serializeObject(o);   
  214.                             }   
  215.                         }   
  216.                     }   
  217.                 }   
  218.             }   
  219.             serialize(o);   
  220.             return sb.join('');   
  221.         },   
  222.         unserialize : function(ss) {   
  223.             var p = 0, ht = [], hv = 1;   
  224.             function unserializeNull() {   
  225.                 p++;   
  226.                 return null;   
  227.             }   
  228.             function unserializeBoolean() {   
  229.                 p++;   
  230.                 var b = (ss.charAt(p++) == '1');   
  231.                 p++;   
  232.                 return b;   
  233.             }   
  234.             function unserializeInteger() {   
  235.                 p++;   
  236.                 var i = parseInt(ss.substring(p, p = ss.indexOf(';', p)));   
  237.                 p++;   
  238.                 return i;   
  239.             }   
  240.             function unserializeDouble() {   
  241.                 p++;   
  242.                 var d = ss.substring(p, p = ss.indexOf(';', p));   
  243.                 switch (d) {   
  244.                     case 'NAN': d = NaN; break;   
  245.                     case 'INF': d = Number.POSITIVE_INFINITY; break;   
  246.                     case '-INF': d = Number.NEGATIVE_INFINITY; break;   
  247.                     default: d = parseFloat(d);   
  248.                 }   
  249.                 p++;   
  250.                 return d;   
  251.             }   
  252.             function unserializeString() {   
  253.                 p++;   
  254.                 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  255.                 p += 2;   
  256.                 var s = ss.substring(p, p += l).toUTF16();   
  257.                 p += 2;   
  258.                 return s;   
  259.             }   
  260.             function unserializeEscapedString(len) {   
  261.                 p++;   
  262.                 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  263.                 p += 2;   
  264.                 var i, sb = new Array(l);   
  265.                 for (i = 0; i < l; i++) {   
  266.                     if ((sb[i] = ss.charAt(p++)) == '\\') {   
  267.                         sb[i] = String.fromCharCode(parseInt(ss.substring(p, p += len), 16));   
  268.                     }   
  269.                 }   
  270.                 p += 2;   
  271.                 return sb.join('');   
  272.             }   
  273.             function unserializeArray() {   
  274.                 p++;   
  275.                 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  276.                 p += 2;   
  277.                 var i, k, a = [];   
  278.                 ht[hv++] = a;   
  279.                 for (i = 0; i < n; i++) {   
  280.                     switch (ss.charAt(p++)) {   
  281.                         case 'i': k = unserializeInteger(); break;   
  282.                         case 's': k = unserializeString(); break;   
  283.                         case 'S': k = unserializeEscapedString(2); break;   
  284.                         case 'U': k = unserializeEscapedString(4); break;   
  285.                         defaultreturn false;   
  286.                     }   
  287.                     a[k] = unserialize();   
  288.                 }   
  289.                 p++;   
  290.                 return a;   
  291.             }   
  292.             function unserializeDate(n) {   
  293.                 var i, k, a = {};   
  294.                 for (i = 0; i < n; i++) {   
  295.                     switch (ss.charAt(p++)) {   
  296.                         case 's': k = unserializeString(); break;   
  297.                         case 'S': k = unserializeEscapedString(2); break;   
  298.                         case 'U': k = unserializeEscapedString(4); break;   
  299.                         defaultreturn false;   
  300.                     }   
  301.                     if (ss.charAt(p++) == 'i') {   
  302.                         a[k] = unserializeInteger();   
  303.                     }   
  304.                     else {   
  305.                         return false;   
  306.                     }   
  307.                 }   
  308.                 p++;   
  309.                 var dt = new Date(   
  310.                     a.year,   
  311.                     a.month - 1,   
  312.                     a.day,   
  313.                     a.hour,   
  314.                     a.minute,   
  315.                     a.second,   
  316.                     a.millisecond   
  317.                 );   
  318.                 ht[hv++] = dt;   
  319.                 return dt;   
  320.             }   
  321.             function unserializeObject() {   
  322.                 p++;   
  323.                 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  324.                 p += 2;   
  325.                 var cn = ss.substring(p, p += l).toUTF16();   
  326.                 p += 2;   
  327.                 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  328.                 p += 2;   
  329.                 if (cn == "PHPRPC_Date") {   
  330.                     return unserializeDate(n);   
  331.                 }   
  332.                 var i, k, o = createObjectOfClass(cn);   
  333.                 ht[hv++] = o;   
  334.                 for (i = 0; i < n; i++) {   
  335.                     switch (ss.charAt(p++)) {   
  336.                         case 's': k = unserializeString(); break;   
  337.                         case 'S': k = unserializeEscapedString(2); break;   
  338.                         case 'U': k = unserializeEscapedString(4); break;   
  339.                         defaultreturn false;   
  340.                     }   
  341.                     if (k.charAt(0) == '\0') {   
  342.                         k = k.substring(k.indexOf('\0', 1) + 1, k.length);   
  343.                     }   
  344.                     o[k] = unserialize();   
  345.                 }   
  346.                 p++;   
  347.                 if (typeof(o.__wakeup) == 'function') o.__wakeup();   
  348.                 return o;   
  349.             }   
  350.             function unserializeCustomObject() {   
  351.                 p++;   
  352.                 var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  353.                 p += 2;   
  354.                 var cn = ss.substring(p, p += l).toUTF16();   
  355.                 p += 2;   
  356.                 var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));   
  357.                 p += 2;   
  358.                 var o = createObjectOfClass(cn);   
  359.                 ht[hv++] = o;   
  360.                 if (typeof(o.unserialize) != 'function') p += n;   
  361.                 else o.unserialize(ss.substring(p, p += n));   
  362.                 p++;   
  363.                 return o;   
  364.             }   
  365.             function unserializeRef() {   
  366.                 p++;   
  367.                 var r = parseInt(ss.substring(p, p = ss.indexOf(';', p)));   
  368.                 p++;   
  369.                 return ht[r];   
  370.             }   
  371.             function getObjectOfClass(cn, poslist, i, c) {   
  372.                 if (i < poslist.length) {   
  373.                     var pos = poslist[i];   
  374.                     cn[pos] = c;   
  375.                     var obj = getObjectOfClass(cn, poslist, i + 1, '.');   
  376.                     if (i + 1 < poslist.length) {   
  377.                         if (obj == null) {   
  378.                             obj = getObjectOfClass(cn, poslist, i + 1, '_');   
  379.                         }   
  380.                     }   
  381.                     return obj;   
  382.                 }   
  383.                 var classname = cn.join('');   
  384.                 try {   
  385.                     return freeEval('new ' + classname + '()');   
  386.                 }   
  387.                 catch (e) {   
  388.                     return null;   
  389.                 }   
  390.             }   
  391.             function createObjectOfClass(classname) {   
  392.                 if (freeEval('typeof(' + classname + ') == "function"')) {   
  393.                     return freeEval('new ' + classname + '()');   
  394.                 }   
  395.                 var poslist = [];   
  396.                 var pos = classname.indexOf("_");   
  397.                 while (pos > -1) {   
  398.                     poslist[poslist.length] = pos;   
  399.                     pos = classname.indexOf("_", pos + 1);   
  400.                 }   
  401.                 if (poslist.length > 0) {   
  402.                     var cn = classname.split('');   
  403.                     var obj = getObjectOfClass(cn, poslist, 0, '.');   
  404.                     if (obj == null) {   
  405.                         obj = getObjectOfClass(cn, poslist, 0, '_');   
  406.                     }   
  407.                     if (obj != null) {   
  408.                         return obj;   
  409.                     }   
  410.                 }   
  411.                 return freeEval('new function ' + classname + '(){};');   
  412.             }   
  413.             function unserialize() {   
  414.                 switch (ss.charAt(p++)) {   
  415.                     case 'N'return ht[hv++] = unserializeNull();   
  416.                     case 'b'return ht[hv++] = unserializeBoolean();   
  417.                     case 'i'return ht[hv++] = unserializeInteger();   
  418.                     case 'd'return ht[hv++] = unserializeDouble();   
  419.                     case 's'return ht[hv++] = unserializeString();   
  420.                     case 'S'return ht[hv++] = unserializeEscapedString(2);   
  421.                     case 'U'return ht[hv++] = unserializeEscapedString(4);   
  422.                     case 'r'return ht[hv++] = unserializeRef();   
  423.                     case 'a'return unserializeArray();   
  424.                     case 'O'return unserializeObject();   
  425.                     case 'C'return unserializeCustomObject();   
  426.                     case 'R'return unserializeRef();   
  427.                     defaultreturn false;   
  428.                 }   
  429.             }   
  430.             return unserialize();   
  431.         }   
  432.     }   
  433. })();  

测试页面:http://test.coolcode.cn/phpserializer

注:如果要把 JavaScript 序列化的内容提交给 PHP 服务器端,需要先进行 base64 编码,然后到服务器端先进行 base64 解码,然后再返序列化,否则中文乱码。

原创作品,禁止转载,欢迎链接。

标签: PHP, JavaScript

« 上一篇 | 下一篇 »

只显示10条记录相关文章

1 条回复至 "JavaScript 版本的 PHP serialize/unserialize 完整实现" 的评论

弱弱的问一句,序列化是做什么的

由 ty 于 2013-08-20 17:56:42 提交#1


发表评论

评论 (必须):