Skip to content

Instantly share code, notes, and snippets.

@abhilashrathod
Created September 22, 2017 09:23
Show Gist options
  • Save abhilashrathod/bb82b112718dd49d62b964ed5384ed53 to your computer and use it in GitHub Desktop.
Save abhilashrathod/bb82b112718dd49d62b964ed5384ed53 to your computer and use it in GitHub Desktop.
this service helps convert php serialized string to json object for angular 2/4 projects.
export class DeserializeService {
idx = 0;
refStack = [];
ridx = 0;
parseNext;
phpstr: string = '';
constructor() {
}
readLength(): any {
let del = this.phpstr.indexOf(':', this.idx);
let val = this.phpstr.substring(this.idx, del);
this.idx = del + 2;
return parseInt(val, 10);
}
readInt(): any {
let del = this.phpstr.indexOf(';', this.idx);
let val = this.phpstr.substring(this.idx, del);
this.idx = del + 1;
return parseInt(val, 10);
}
parseAsInt(): any {
let val = this.readInt();
this.refStack[this.ridx++] = val;
return val;
} //end parseAsInt
parseAsFloat(): any {
let del = this.phpstr.indexOf(';', this.idx);
let val:any = this.phpstr.substring(this.idx, del);
this.idx = del + 1;
val = parseFloat(val);
this.refStack[this.ridx++] = val;
return val;
} //end parseAsFloat
parseAsBoolean(): any {
let del = this.phpstr.indexOf(';', this.idx);
let val:any = this.phpstr.substring(this.idx, del);
this.idx = del + 1;
val = ("1" === val) ? true : false;
this.refStack[this.ridx++] = val;
return val;
}
readString(): any {
let len = this.readLength()
, utfLen = 0
, bytes = 0
, ch
, val;
while (bytes < len) {
ch = this.phpstr.charCodeAt(this.idx + utfLen++);
if (ch <= 0x007F) {
bytes++;
} else if (ch > 0x07FF) {
bytes += 3;
} else {
bytes += 2;
}
}
val = this.phpstr.substring(this.idx, this.idx + utfLen);
this.idx += utfLen + 2;
return val;
} //end readString
parseAsString(): any {
let val = this.readString();
this.refStack[this.ridx++] = val;
return val;
} //end parseAsString
readType(): any {
let type = this.phpstr.charAt(this.idx);
this.idx += 2;
return type;
} //end readType
readKey(): any {
let type = this.readType();
switch (type) {
case 'i':
return this.readInt();
case 's':
return this.readString();
default:
throw {
name: "Parse Error",
message: "Unknown key type '" + type + "' at position " +
(this.idx - 2)
};
} //end switch
}
parseAsArray(): any {
let len = this.readLength()
, resultArray = []
, resultHash = {}
, keep:any = resultArray
, lref = this.ridx++
, key
, val
, i
, j
, alen;
this.refStack[lref] = keep;
for (i = 0; i < len; i++) {
key = this.readKey();
val = this.parseNext();
if (keep === resultArray && parseInt(key, 10) === i) {
// store in array version
resultArray.push(val);
} else {
if (keep !== resultHash) {
// found first non-sequential numeric key
// convert existing data to hash
for (j = 0, alen = resultArray.length; j < alen; j++) {
resultHash[j] = resultArray[j];
}
keep = resultHash;
this.refStack[lref] = keep;
}
resultHash[key] = val;
} //end if
} //end for
this.idx++;
return keep;
} //end parseAsArray
fixPropertyName = function (parsedName, baseClassName) {
let class_name
, prop_name
, pos;
if ("\u0000" === parsedName.charAt(0)) {
// "<NUL>*<NUL>property"
// "<NUL>class<NUL>property"
pos = parsedName.indexOf("\u0000", 1);
if (pos > 0) {
class_name = parsedName.substring(1, pos);
prop_name = parsedName.substr(pos + 1);
if ("*" === class_name) {
// protected
return prop_name;
} else if (baseClassName === class_name) {
// own private
return prop_name;
} else {
// private of a descendant
return class_name + "::" + prop_name;
// On the one hand, we need to prefix property name with
// class name, because parent and child classes both may
// have private property with same name. We don't want
// just to overwrite it and lose something.
//
// On the other hand, property name can be "foo::bar"
//
// $obj = new stdClass();
// $obj->{"foo::bar"} = 42;
// // any user-defined class can do this by default
//
// and such property also can overwrite something.
//
// So, we can to lose something in any way.
}
}
} else {
// public "property"
return parsedName;
}
};
parseAsObject(): any {
let len
, obj = {}
, lref = this.ridx++
// HACK last char after closing quote is ':',
// but not ';' as for normal string
, clazzname = this.readString()
, key
, val
, i;
this.refStack[lref] = obj;
len = this.readLength();
for (i = 0; i < len; i++) {
key = this.fixPropertyName(this.readKey(), clazzname);
val = this.parseNext();
obj[key] = val;
}
this.idx++;
return obj;
} //end parseAsObject
parseAsCustom(): any {
let clazzname = this.readString()
, content = this.readString();
return {
"__PHP_Incomplete_Class_Name": clazzname,
"serialized": content
};
} //end parseAsCustom
parseAsRefValue(): any {
let ref = this.readInt()
// php's ref counter is 1-based; our stack is 0-based.
, val = this.refStack[ref - 1];
this.refStack[this.ridx++] = val;
return val;
} //end parseAsRefValue
parseAsRef(): any {
let ref = this.readInt();
// php's ref counter is 1-based; our stack is 0-based.
return this.refStack[ref - 1];
} //end parseAsRef
parseAsNull(): any {
let val = null;
this.refStack[this.ridx++] = val;
return val;
}; //end parseAsNull
deserizlize(phpstr): string {
this.phpstr = phpstr;
this.parseNext = function () {
let type = this.readType();
switch (type) {
case 'i':
return this.parseAsInt();
case 'd':
return this.parseAsFloat();
case 'b':
return this.parseAsBoolean();
case 's':
return this.parseAsString();
case 'a':
return this.parseAsArray();
case 'O':
return this.parseAsObject();
case 'C':
return this.parseAsCustom();
// link to object, which is a value - affects refStack
case 'r':
return this.parseAsRefValue();
// PHP's reference - DOES NOT affect refStack
case 'R':
return this.parseAsRef();
case 'N':
return this.parseAsNull();
default:
throw {
name: "Parse Error",
message: "Unknown type '" + type + "' at position " + (this.idx - 2)
};
} //end switch
}; //end parseNext
return this.parseNext();
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment