Unserialize PHP Array in Javascript

I have a table with loading rows of serialized arrays that I plan to query and pass it to JavaScript .

The problem is unserialize with javascript rather than php?

Otherwise, I will have to load all the lines, loop them and uneserialize and assign them to a temporary PHP array, and then json_encode go back to JavaScript, which seems extremely inefficient if I can send the data still serialized so that JavaScript doesn't serialize the data when it's necessary.

Is there a built-in Javascript function that does this, or will I have to loop the lines in PHP before I encode it?

Note. I do not use jQuery.

EDIT: An example of my serialized data in PHP from my table:

 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;}} 
+8
source share
4 answers

wrap json_encode around unserialize

 echo json_encode( unserialize( $array)); 
+5
source

Php.js has a javascript implementation of non-serialization and serialization:

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

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

However, it may be more efficient to convert to server-side JSON. JSON.parse will be much faster than PHP.js unserialize.

+12
source

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

Just noticed your comment, so we go:

in php

 json_encode(unserialize(SerializedVal)); 

in javascript:

 JSON.parse(JsonString); 
+3
source

I thought that I needed to write a JS function that can deserialize data serialized in PHP.

But before moving on to this solution, keep in mind that:

  • The format created by the serialize PHP function is specific to PHP, so the best option is to use PHP unserialize to have a 100% guarantee that it does its job correctly.
  • PHP can store class information in these lines and even output some custom serialization methods. So to deserialize such strings, you need to know about these classes and methods.
  • PHP data structures do not correspond to 1-to-1 JavaScript data structures: PHP associative arrays can have strings as keys, and therefore they are more like JavaScript objects than JS arrays, but in PHP keys retain the insertion order, and keys can have truly a numeric data type that is not possible for JS objects. We can say that then we should look at Map objects in JS, but they allow us to store 13 and "13" as separate keys, which PHP does not allow. And here we touch only the tip of the iceberg ...
  • PHP serializes the protected and private properties of objects, which is not only strange (how private is it?), But are concepts that (so far) do not exist in JS, or at least not. If someone somehow implements (hard) private properties in JS, then how can some deserialization set such a private property?
  • JSON is an alternative that is not specific to PHP, and does not care about custom classes. If you can access the PHP source where the serialization takes place, then change it so that JSON is created instead. PHP offers json_encode for this, and JavaScript has JSON.parse for decoding it. This is definitely the way to go if you can.

If with these comments you still see the need for the JS unserialise function, then read on.

Here is a JS implementation that provides methods to the PHP object similar to the built-in JSON object: parse and stringify .

When the input of the parse method refers to a class, it will first check to see if you passed a reference to that class in the (optional) second argument. If not, a layout will be created for this class (to avoid unwanted side effects). In any case, an instance of this class will be created. If the input line indicates that custom serialization has occurred, the unserialize method for this instance of the object will be called. You must provide logic in this method, since the line itself does not provide information on how this should be done. This is known only in the PHP code that generated this line.

This implementation also supports circular references. When the associative array turns out to be a sequential array, the JS array is returned.

 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 // eg { 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 } )); 
+1
source

All Articles