在 Javascript 中反序列化 PHP 数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14227388/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unserialize PHP Array in Javascript
提问by Sir
I have a table with a load of rows of serialized arrays that I plan to request and pass it to JavaScript.
我有一个包含大量序列化数组行的表,我计划请求并将其传递给JavaScript.
The problem is - is it possible to unserializewith JavaScript rather than PHP ?
问题是 - 是否可以unserialize使用 JavaScript 而不是 PHP ?
Otherwise I will have to load all the rows, loop them and unserialize them and assign them to a temporary PHP array and then json_encode it back to JavaScript which seems highly inefficient if I can send the data still serialized so that JavaScript can unserialize the data when it needs to.
否则我将不得不加载所有行,循环它们并反序列化它们并将它们分配给一个临时的 PHP 数组,然后将其 json_encode 返回到 JavaScript,如果我可以发送仍然序列化的数据以便 JavaScript 可以反序列化数据,这似乎非常低效它需要。
Is there a built in Javascript function that does it or will I have to loop the rows in PHP before I encode it?
是否有内置的 Javascript 函数可以执行此操作,还是必须在编码之前循环 PHP 中的行?
Note I am not using jQuery.
注意我没有使用 jQuery。
EDIT: Example of my serialized data in PHP from my table:
编辑:我的表中 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;}}
采纳答案by charlietfl
wrap json_encodearound unserialize
包裹json_encode周围unserialize
echo json_encode( unserialize( $array));
回答by snostorm
Php.js has javascript implementations of unserialize and serialize:
Php.js 有反序列化和序列化的 javascript 实现:
http://phpjs.org/functions/unserialize/
http://phpjs.org/functions/unserialize/
http://phpjs.org/functions/serialize/
http://phpjs.org/functions/serialize/
That said, it's probably more efficient to convert to JSON on the server side. JSON.parse is going to be a lot faster than PHP.js's unserialize.
也就是说,在服务器端转换为 JSON 可能更有效。JSON.parse 将比 PHP.js 的反序列化快得多。
回答by HMR
http://php.net/manual/en/book.json.php
http://php.net/manual/en/book.json.php
Just noticed your comment so here we go:
刚刚注意到你的评论,所以我们开始:
in PHP
在 PHP 中
json_encode(unserialize(SerializedVal));
in JavaScript:
在 JavaScript 中:
JSON.parse(JsonString);
回答by trincot
I thought I would have a go at writing a JS function that can unserialize PHP-serialized data.
我想我会尝试编写一个可以反序列化 PHP 序列化数据的 JS 函数。
But before going for this solution please be aware that:
但在使用此解决方案之前,请注意:
- The format produced by PHP's
serializefunction is PHP specific and so the best option is to use PHP'sunserializeto have 100% guarantee that it is doing the job right. - PHP can store class information in those strings and even the output of some custom serialization methods. So to unserialize such strings you would need to know about those classes and methods.
- PHP data structures do not correspond 1-to-1 to JavaScript data structures: PHP associative arrays can have strings as keys, and so they look more like JavaScript objects than JS arrays, but in PHP the keys keep insertion order, and keys can have a truly numeric data type which is not possible with JS objects. One could say that then we should look at
Mapobjects in JS, but those allow to store 13 and "13" as separate keys, which PHP does not allow. And we're only touching the tip of the iceberg here... - PHP serializes protected and private properties of objects, which not only is strange (how privateis that?), but are concepts which do not (yet) exist in JS, or at least not in the same way. If one would somehow implement (hard) private properties in JS, then how would some unserialization be ableto setsuch a private property?
- JSON is an alternative that is not specific to PHP, and does not care about custom classes either. If you can access the PHP source of were the serialization happens, then change this to produce JSON instead. PHP offers
json_encodefor that, and JavaScript hasJSON.parseto decode it. This is certainly the way to go if you can.
- PHP
serialize函数生成的格式是特定于 PHP 的,因此最好的选择是使用 PHPunserialize以 100% 保证它正确地完成工作。 - PHP 可以在这些字符串中存储类信息,甚至可以存储一些自定义序列化方法的输出。因此,要反序列化此类字符串,您需要了解这些类和方法。
- PHP 数据结构并不与 JavaScript 数据结构一一对应:PHP 关联数组可以将字符串作为键,因此它们看起来更像 JavaScript 对象而不是 JS 数组,但在 PHP 中键保持插入顺序,键可以有JS 对象无法实现的真正数字数据类型。可以说,那么我们应该看看
MapJS中的对象,但那些允许将 13 和“13”存储为单独的键,这是 PHP 不允许的。而我们只是触及了这里的冰山一角...... - PHP 序列化对象的受保护和私有属性,这不仅很奇怪(这有多私有?),而且是 JS 中(还)不存在的概念,或者至少不以相同的方式存在。如果一个人会以某种方式实现JS(硬)私人财产,那么如何将一些反序列化是能够以设定这样的私有财产?
- JSON 是一种不特定于 PHP 的替代方案,也不关心自定义类。如果您可以访问发生序列化的 PHP 源代码,则将其更改为生成 JSON。PHP 提供
json_encode了这一点,而 JavaScript 必须对其JSON.parse进行解码。如果可以的话,这当然是要走的路。
If with those remarks you still see the need for a JS unserialise function, then read on.
如果根据这些评论,您仍然认为需要 JS 反序列化函数,请继续阅读。
Here is a JS implementation that provides a PHPobject with similar methods as the built-in JSONobject: parseand stringify.
这是一个 JS 实现,它提供了一个PHP与内置对象具有类似方法的JSON对象:parse和stringify。
When an input to the parsemethod references a class, then it will first check if you passed a reference to that class in the (optional) second argument. If not, a mock will be created for that class (to avoid undesired side-effects). In either case an instance of that class will be created. If the input string specifies a customserialization happened then the method unserializeon that object instance will be called. You must provide the logic in that method as the string itself does not give information about how that should be done. It is only known in the PHP code that generatedthat string.
当该parse方法的输入引用一个类时,它将首先检查您是否在(可选)第二个参数中传递了对该类的引用。如果没有,将为该类创建一个模拟(以避免不希望的副作用)。在任何一种情况下,都会创建该类的一个实例。如果输入字符串指定发生了自定义序列化unserialize,则将调用该对象实例上的方法。您必须在该方法中提供逻辑,因为字符串本身不提供有关如何完成的信息。只有在生成该字符串的 PHP 代码中才知道它。
This implementation also supports cyclic references. When an associative array turns out to be a sequential array, then a JS array will be returned.
此实现还支持循环引用。当关联数组变成顺序数组时,将返回一个 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/, ".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 } ));

