【问题标题】:Unserialize PHP Array in Javascript在 Javascript 中反序列化 PHP 数组
【发布时间】:2012-12-23 00:54:47
【问题描述】:

我有一个表,其中包含我计划请求的大量序列化数组行并将其传递给JavaScript

问题是 - 是否可以使用 JavaScript 而不是 PHP 来 unserialize

否则我将不得不加载所有行,循环它们并反序列化它们并将它们分配给一个临时 PHP 数组,然后将其 json_encode 回 JavaScript,如果我可以发送仍然序列化的数据以便 JavaScript 可以反序列化,这似乎非常低效需要时获取数据。

是否有一个内置的 Javascript 函数可以做到这一点,或者我必须在编码之前在 PHP 中循环这些行?

注意我没有使用 jQuery。

编辑: 我的表中的 PHP 序列化数据示例:

a:8:{i:0;a:2:{i:0;i:10;i:1;i:11;}i:1;a:2:{i:0;i:9;i:1;i:11;}i:2;a:2:
{i:0;i:8;i:1;i:11;}i:3;a:2:{i:0;i:8;i:1;i:10;}i:4;a:2:{i:0;i:8;i:1;i:9;}i:5;a:2:
{i:0;i:8;i:1;i:8;}i:6;a:2:{i:0;i:8;i:1;i:7;}i:7;a:2:{i:0;i:8;i:1;i:6;}}

【问题讨论】:

  • 可以用json_encode代替serialize吗?在 PHP 和 JS 之间进行通信时,这可能更容易使用。 php.net/manual/en/function.json-encode.php
  • 我先将数组序列化,然后再将它们存储到我的数据库中,这样我就可以存储它并节省空间。我不认为 json_encode 会完成这项工作。
  • @Dave 但是你可以在 PHP 中反序列化然后 JSON 编码。
  • 可能在检索后反序列化,然后作为 json 发送?此外,序列化会添加大量额外数据,因此从长远来看,您可能不会节省太多空间。
  • 所以我唯一的选择是循环我选择的所有行以反序列化特定字段,然后用该数据编码一个临时数组?那是非常低效的:(

标签: php javascript ajax


【解决方案1】:

Php.js 有反序列化和序列化的 javascript 实现:

http://phpjs.org/functions/unserialize/

http://phpjs.org/functions/serialize/

也就是说,在服务器端转换为 JSON 可能更有效。 JSON.parse 会比 PHP.js 的反序列化快很多。

【讨论】:

    【解决方案2】:

    我想我会尝试编写一个可以反序列化 PHP 序列化数据的 JS 函数。

    但在采用此解决方案之前,请注意:

    • PHP 的 serialize 函数生成的格式是 PHP 特定的,因此最好的选择是使用 PHP 的 unserialize 以 100% 保证它正确地完成工作。
    • PHP 可以将类信息存储在这些字符串中,甚至可以存储一些自定义序列化方法的输出。因此,要反序列化此类字符串,您需要了解这些类和方法。
    • PHP 数据结构不与 JavaScript 数据结构一一对应:PHP 关联数组可以将字符串作为键,因此它们看起来更像 JavaScript 对象而不是 JS 数组,但在 PHP 中键保持插入顺序,并且键可以具有真正的数字数据类型,这对于 JS 对象是不可能的。有人可能会说,那么我们应该查看 JS 中的 Map 对象,但是那些允许将 13 和 "13" 作为单独的键存储,而 PHP 不允许这样做。而我们在这里只是触及了冰山一角......
    • PHP 序列化对象的受保护和私有属性,这不仅很奇怪(private 是怎么回事?),而且是(尚)不存在于 JS 中的概念,或者至少不存在于以同样的方式。如果有人会以某种方式在 JS 中实现(硬)私有属性,那么如何能够设置这样一个私有属性?
    • JSON 是一种不特定于 PHP 的替代方案,也不关心自定义类。如果您可以访问发生序列化的 PHP 源,则将其更改为生成 JSON。 PHP 为此提供了json_encode,而JavaScript 提供了JSON.parse 对其进行解码。如果可以的话,这当然是要走的路。

    如果您仍然认为需要 JS 反序列化函数,请继续阅读。

    这是一个 JS 实现,它提供了一个 PHP 对象,其方法与内置 JSON 对象类似:parsestringify

    parse 方法的输入引用一个类时,它会首先检查您是否在(可选)第二个参数中传递了对该类的引用。如果没有,将为该类创建一个模拟(以避免不希望的副作用)。在任何一种情况下,都会创建该类的实例。如果输入字符串指定发生了 custom 序列化,则将调用该对象实例上的方法 unserialize。您必须提供该方法中的逻辑,因为字符串本身并未提供有关如何完成此操作的信息。只有在 生成该字符串的 PHP 代码中才知道。

    此实现还支持循环引用。当关联数组变成顺序数组时,会返回一个JS数组。

    const PHP = {
        stdClass: function() {},
        stringify(val) {
            const hash = new Map([[Infinity, "d:INF;"], [-Infinity, "d:-INF;"], [NaN, "d:NAN;"], [null, "N;"], [undefined, "N;"]]); 
            const utf8length = str => str ? encodeURI(str).match(/(%.)?./g).length : 0;
            const serializeString = (s,delim='"') => `${utf8length(s)}:${delim[0]}${s}${delim[delim.length-1]}`;
            let ref = 0;
            
            function serialize(val, canReference = true) {
                if (hash.has(val)) return hash.get(val);
                ref += canReference;
                if (typeof val === "string") return `s:${serializeString(val)};`;
                if (typeof val === "number") return  `${Math.round(val) === val ? "i" : "d"}:${(""+val).toUpperCase().replace(/(-?\d)E/, "$1.0E")};`;
                if (typeof val === "boolean") return  `b:${+val};`;
                const a = Array.isArray(val) || val.constructor === Object;
                hash.set(val, `${"rR"[+a]}:${ref};`);
                if (typeof val.serialize === "function") {
                    return `C:${serializeString(val.constructor.name)}:${serializeString(val.serialize(), "{}")}`;
                }
                const vals = Object.entries(val).filter(([k, v]) => typeof v !== "function");
                return (a ? "a" : `O:${serializeString(val.constructor.name)}`) 
                    + `:${vals.length}:{${vals.map(([k, v]) => serialize(a && /^\d{1,16}$/.test(k) ? +k : k, false) + serialize(v)).join("")}}`;
            }
            return serialize(val);
        },
        // Provide in second argument the classes that may be instantiated
        //  e.g.  { MyClass1, MyClass2 }
        parse(str, allowedClasses = {}) {
            allowedClasses.stdClass = PHP.stdClass; // Always allowed.
            let offset = 0;
            const values = [null];
            const specialNums = { "INF": Infinity, "-INF": -Infinity, "NAN": NaN };
    
            const kick = (msg, i = offset) => { throw new Error(`Error at ${i}: ${msg}\n${str}\n${" ".repeat(i)}^`) }
            const read = (expected, ret) => expected === str.slice(offset, offset+=expected.length) ? ret 
                                             : kick(`Expected '${expected}'`, offset-expected.length);
            
            function readMatch(regex, msg, terminator=";") {
                read(":");
                const match = regex.exec(str.slice(offset));
                if (!match) kick(`Exected ${msg}, but got '${str.slice(offset).match(/^[:;{}]|[^:;{}]*/)[0]}'`);
                offset += match[0].length;
                return read(terminator, match[0]);
            }
            
            function readUtf8chars(numUtf8Bytes, terminator="") {
                const i = offset;
                while (numUtf8Bytes > 0) {
                    const code = str.charCodeAt(offset++);
                    numUtf8Bytes -= code < 0x80 ? 1 : code < 0x800 || code>>11 === 0x1B ? 2 : 3;
                }
                return numUtf8Bytes ? kick("Invalid string length", i-2) : read(terminator, str.slice(i, offset));
            }
            
            const create = className => !className ? {}
                        : allowedClasses[className] ? Object.create(allowedClasses[className].prototype)
                        : new {[className]: function() {} }[className]; // Create a mock class for this name
            const readBoolean = () => readMatch(/^[01]/, "a '0' or '1'", ";");
            const readInt     = () => +readMatch(/^-?\d+/, "an integer", ";");
            const readUInt    = terminator => +readMatch(/^\d+/, "an unsigned integer", terminator);
            const readString  = (terminator="") => readUtf8chars(readUInt(':"'), '"'+terminator);
            
            function readDecimal() {
                const num = readMatch(/^-?(\d+(\.\d+)?(E[+-]\d+)?|INF)|NAN/, "a decimal number", ";");
                return num in specialNums ? specialNums[num] : +num;
            }
            
            function readKey() {
                const typ = str[offset++];
                return typ === "s" ? readString(";") 
                     : typ === "i" ? readUInt(";")
                     : kick("Expected 's' or 'i' as type for a key, but got ${str[offset-1]}", offset-1);
            }
           
            function readObject(obj) {
                for (let i = 0, length = readUInt(":{"); i < length; i++) obj[readKey()] = readValue();
                return read("}", obj);
            }
            
            function readArray() {
                const obj = readObject({});
                return Object.keys(obj).some((key, i) => key != i) ? obj : Object.values(obj);
            }
            
            function readCustomObject(obj) {
                if (typeof obj.unserialize !== "function") kick(`Instance of ${obj.constructor.name} does not have an "unserialize" method`);
                obj.unserialize(readUtf8chars(readUInt(":{")));
                return read("}", obj);
            }
            
            function readValue() {
                const typ = str[offset++].toLowerCase();
                const ref = values.push(null)-1;
                const val = typ === "n" ? read(";", null)
                          : typ === "s" ? readString(";")
                          : typ === "b" ? readBoolean()
                          : typ === "i" ? readInt()
                          : typ === "d" ? readDecimal()
                          : typ === "a" ? readArray()                            // Associative array
                          : typ === "o" ? readObject(create(readString()))       // Object
                          : typ === "c" ? readCustomObject(create(readString())) // Custom serialized object
                          : typ === "r" ? values[readInt()]                      // Backreference
                          : kick(`Unexpected type ${typ}`, offset-1);
                if (typ !== "r") values[ref] = val;
                return val;
            }
            
            const val = readValue();
            if (offset !== str.length) kick("Unexpected trailing character");
            return val;
        }
    }
    /**************** EXAMPLE USES ************************/
    
    // Unserialize a sequential array
    console.log(PHP.parse('a:4:{i:0;s:4:"This";i:1;s:2:"is";i:2;s:2:"an";i:3;s:5:"array";}'));
    
    // Unserialize an associative array into an object
    console.log(PHP.parse('a:2:{s:8:"language";s:3:"PHP";s:7:"version";d:7.1;}'));
    
    // Example with class that has custom serialize function:
    var MyClass = (function () {
        const priv = new WeakMap(); // This is a way to implement private properties in ES6
        return class MyClass {
            constructor() {
                priv.set(this, "");
                this.wordCount = 0;
            }
            unserialize(serialised) {
                const words = PHP.parse(serialised);
                priv.set(this, words);
                this.wordCount = words.split(" ").length;
            }
            serialize() {
                return PHP.stringify(priv.get(this));
            }
        }
    })();
    
    // Unserialise a PHP string that needs the above class to work, and will call its unserialize method
    // The class needs to be passed as object key/value as second argument, so to allow this side effect to happen:
    console.log(PHP.parse('C:7:"MyClass":23:{s:15:"My private data";}', { MyClass } ));

    【讨论】:

      【解决方案3】:

      json_encode 包裹在unserialize 周围

      echo json_encode( unserialize( $array));
      

      【讨论】:

      • 如果我从包含此类数据的表中选择多行怎么办?
      • 那么您需要将所有行推入另一个数组并发送完整数组
      • 该死的,这是我出于性能原因希望避免的
      • performace reasons?它与循环遍历行以生成其他输出(例如 html)或从字段创建 json 数组有什么不同?
      【解决方案4】:

      http://php.net/manual/en/book.json.php

      刚刚注意到您的评论,所以我们开始:

      在 PHP 中

      json_encode(unserialize(SerializedVal));
      

      在 JavaScript 中:

      JSON.parse(JsonString);
      

      【讨论】:

      • 当 PHP 序列化时,我们无法使用 javascript json 解析对其进行解析/反序列化。具有讽刺意味的是,所有 SO 问题都有相同的答案 JSON.parse。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-11
      • 1970-01-01
      • 1970-01-01
      • 2014-05-21
      • 1970-01-01
      • 1970-01-01
      • 2014-07-14
      相关资源
      最近更新 更多