2018-05-21 12:35:11 +00:00
/**
* @author Jarmo van Lenthe [github.com/jarmovanlenthe]
* @copyright Jarmo van Lenthe
* @license Apache-2.0
*/
2019-07-09 12:23:59 +01:00
import Operation from "../Operation.mjs" ;
import OperationError from "../errors/OperationError.mjs" ;
2018-05-21 12:35:11 +00:00
/**
* PHP Deserialize operation
*/
class PHPDeserialize extends Operation {
/**
* PHPDeserialize constructor
*/
constructor () {
super ();
this . name = "PHP Deserialize" ;
this . module = "Default" ;
this . description = "Deserializes PHP serialized data, outputting keyed arrays as JSON.<br><br>This function does not support <code>object</code> tags.<br><br>Example:<br><code>a:2:{s:1:"a";i:10;i:0;a:1:{s:2:"ab";b:1;}}</code><br>becomes<br><code>{"a": 10,0: {"ab": true}}</code><br><br><u>Output valid JSON:</u> JSON doesn't support integers as keys, whereas PHP serialization does. Enabling this will cast these integers to strings. This will also escape backslashes." ;
2018-08-21 19:07:13 +01:00
this . infoURL = "http://www.phpinternalsbook.com/classes_objects/serialization.html" ;
2018-05-21 12:35:11 +00:00
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
"name" : "Output valid JSON" ,
"type" : "boolean" ,
"value" : true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run ( input , args ) {
/**
* Recursive method for deserializing.
* @returns {*}
*/
function handleInput () {
/**
* Read `length` characters from the input, shifting them out the input.
* @param length
* @returns {string}
*/
function read ( length ) {
let result = "" ;
for ( let idx = 0 ; idx < length ; idx ++ ) {
const char = inputPart . shift ();
if ( char === undefined ) {
throw new OperationError ( "End of input reached before end of script" );
}
result += char ;
}
return result ;
}
/**
* Read characters from the input until `until` is found.
* @param until
* @returns {string}
*/
function readUntil ( until ) {
let result = "" ;
for (;;) {
const char = read ( 1 );
if ( char === until ) {
break ;
} else {
result += char ;
}
}
return result ;
}
/**
* Read characters from the input that must be equal to `expect`
* @param expect
* @returns {string}
*/
function expect ( expect ) {
const result = read ( expect . length );
if ( result !== expect ) {
throw new OperationError ( "Unexpected input found" );
}
return result ;
}
/**
* Helper function to handle deserialized arrays.
* @returns {Array}
*/
function handleArray () {
const items = parseInt ( readUntil ( ":" ), 10 ) * 2 ;
expect ( "{" );
const result = [];
let isKey = true ;
let lastItem = null ;
for ( let idx = 0 ; idx < items ; idx ++ ) {
const item = handleInput ();
if ( isKey ) {
lastItem = item ;
isKey = false ;
} else {
const numberCheck = lastItem . match ( /[0-9]+/ );
if ( args [ 0 ] && numberCheck && numberCheck [ 0 ]. length === lastItem . length ) {
2021-02-10 13:13:19 +00:00
result . push ( '"' + lastItem + '": ' + item );
2018-05-21 12:35:11 +00:00
} else {
result . push ( lastItem + ": " + item );
}
isKey = true ;
}
}
expect ( "}" );
return result ;
}
const kind = read ( 1 ). toLowerCase ();
switch ( kind ) {
case "n" :
expect ( ";" );
2019-10-01 21:07:26 -08:00
return "null" ;
2018-05-21 12:35:11 +00:00
case "i" :
case "d" :
case "b" : {
expect ( ":" );
const data = readUntil ( ";" );
if ( kind === "b" ) {
return ( parseInt ( data , 10 ) !== 0 );
}
return data ;
}
case "a" :
expect ( ":" );
return "{" + handleArray () + "}" ;
case "s" : {
expect ( ":" );
const length = readUntil ( ":" );
expect ( "\"" );
const value = read ( length );
2021-02-10 13:13:19 +00:00
expect ( '";' );
2018-05-21 12:35:11 +00:00
if ( args [ 0 ]) {
2021-02-10 13:13:19 +00:00
return '"' + value . replace ( /"/g , '\\"' ) + '"' ; // lgtm [js/incomplete-sanitization]
2018-05-21 12:35:11 +00:00
} else {
2021-02-10 13:13:19 +00:00
return '"' + value + '"' ;
2018-05-21 12:35:11 +00:00
}
}
default :
throw new OperationError ( "Unknown type: " + kind );
}
}
const inputPart = input . split ( "" );
return handleInput ();
}
}
export default PHPDeserialize ;