Skip to content

Commit bf1e708

Browse files
committed
added PHP Serialize operation
1 parent 1bc8872 commit bf1e708

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/**
2+
* @author brun0ne [brunonblok@gmail.com]
3+
* @copyright Crown Copyright 2023
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
10+
/**
11+
* PHP Serialize operation
12+
*/
13+
class PHPSerialize extends Operation {
14+
15+
/**
16+
* PHPSerialize constructor
17+
*/
18+
constructor() {
19+
super();
20+
21+
this.name = "PHP Serialize";
22+
this.module = "Default";
23+
this.description = "Performs PHP serialization on JSON data.<br><br>This function does not support <code>object</code> tags.<br><br>Since PHP doesn't distinguish dicts and arrays, this operation is not always symmetric to <code>PHP Deserialize</code>.<br><br>Example:<br><code>[5,&quot;abc&quot;,true]</code><br>becomes<br><code>a:3:{i:0;i:5;i:1;s:3:&quot;abc&quot;;i:2;b:1;}<code>";
24+
this.infoURL = "https://www.phpinternalsbook.com/php5/classes_objects/serialization.html";
25+
this.inputType = "JSON";
26+
this.outputType = "string";
27+
this.args = [];
28+
}
29+
30+
/**
31+
* @param {JSON} input
32+
* @param {Object[]} args
33+
* @returns {string}
34+
*/
35+
run(input, args) {
36+
/**
37+
* Determines if a number is an integer
38+
* @param {number} value
39+
* @returns {boolean}
40+
*/
41+
function isInteger(value) {
42+
return typeof value === "number" && parseInt(value.toString(), 10) === value;
43+
}
44+
45+
/**
46+
* Serialize basic types
47+
* @param {string | number | boolean} content
48+
* @returns {string}
49+
*/
50+
function serializeBasicTypes(content) {
51+
const basicTypes = {
52+
"string": "s",
53+
"integer": "i",
54+
"float": "d",
55+
"boolean": "b"
56+
};
57+
58+
/**
59+
* Booleans
60+
* cast to 0 or 1
61+
*/
62+
if (typeof content === "boolean"){
63+
return `${basicTypes["boolean"]}:${content ? 1 : 0}`;
64+
}
65+
66+
/**
67+
* Numbers
68+
*/
69+
if (typeof content === "number"){
70+
if (isInteger(content)){
71+
return `${basicTypes["integer"]}:${content.toString()}`
72+
}
73+
else {
74+
return `${basicTypes["float"]}:${content.toString()}`
75+
}
76+
}
77+
78+
/**
79+
* Strings
80+
*/
81+
if (typeof content === "string")
82+
return `${basicTypes["string"]}:${content.length}:"${content}"`;
83+
84+
/** This should be unreachable */
85+
throw new OperationError(`Encountered a non-implemented type: ${typeof content}`);
86+
}
87+
88+
/**
89+
* Recursively serialize
90+
* @param {*} object
91+
* @returns {string}
92+
*/
93+
function serialize(object) {
94+
/**
95+
* Null
96+
*/
97+
if (object == null) {
98+
return `N;`
99+
}
100+
101+
/**
102+
* Basic types
103+
*/
104+
if (typeof object !== "object"){
105+
return `${serializeBasicTypes(object)};`;
106+
}
107+
108+
/**
109+
* Arrays
110+
*/
111+
else if (object instanceof Array) {
112+
const serializedElements = [];
113+
114+
for (let i = 0; i < object.length; i++) {
115+
serializedElements.push(`${serialize(i)}${serialize(object[i])}`);
116+
}
117+
118+
return `a:${object.length}:{${serializedElements.join("")}}`
119+
}
120+
121+
/**
122+
* Objects
123+
* Note: the output cannot be guaranteed to be in the same order as the input
124+
*/
125+
else if (object instanceof Object) {
126+
const serializedElements = [];
127+
const keys = Object.keys(object);
128+
129+
for (const key of keys) {
130+
serializedElements.push(`${serialize(key)}${serialize(object[key])}`);
131+
}
132+
133+
return `a:${keys.length}:{${serializedElements.join("")}}`
134+
}
135+
136+
/** This should be unreachable */
137+
throw new OperationError(`Encountered a non-implemented type: ${typeof object}`);
138+
}
139+
140+
return serialize(input);
141+
}
142+
}
143+
144+
export default PHPSerialize;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* PHP Serialization tests.
3+
*
4+
* @author brun0ne [brunonblok@gmail.com]
5+
*
6+
* @copyright Crown Copyright 2023
7+
* @license Apache-2.0
8+
*/
9+
10+
import TestRegister from "../../lib/TestRegister.mjs";
11+
12+
TestRegister.addTests([
13+
{
14+
name: "PHP Serialize empty array",
15+
input: "[]",
16+
expectedOutput: "a:0:{}",
17+
recipeConfig: [
18+
{
19+
op: "PHP Serialize",
20+
args: []
21+
}
22+
]
23+
},
24+
{
25+
name: "PHP Serialize empty object",
26+
input: "{}",
27+
expectedOutput: "a:0:{}",
28+
recipeConfig: [
29+
{
30+
op: "PHP Serialize",
31+
args: []
32+
}
33+
]
34+
},
35+
{
36+
name: "PHP Serialize null",
37+
input: "null",
38+
expectedOutput: "N;",
39+
recipeConfig: [
40+
{
41+
op: "PHP Serialize",
42+
args: []
43+
}
44+
]
45+
},
46+
{
47+
name: "PHP Serialize integer",
48+
input: "10",
49+
expectedOutput: "i:10;",
50+
recipeConfig: [
51+
{
52+
op: "PHP Serialize",
53+
args: []
54+
}
55+
]
56+
},
57+
{
58+
name: "PHP Serialize float",
59+
input: "14.523",
60+
expectedOutput: "d:14.523;",
61+
recipeConfig: [
62+
{
63+
op: "PHP Serialize",
64+
args: []
65+
}
66+
]
67+
},
68+
{
69+
name: "PHP Serialize boolean",
70+
input: "[true, false]",
71+
expectedOutput: "a:2:{i:0;b:1;i:1;b:0;}",
72+
recipeConfig: [
73+
{
74+
op: "PHP Serialize",
75+
args: []
76+
}
77+
]
78+
},
79+
{
80+
name: "PHP Serialize string",
81+
input: "\"Test string to serialize\"",
82+
expectedOutput: "s:24:\"Test string to serialize\";",
83+
recipeConfig: [
84+
{
85+
op: "PHP Serialize",
86+
args: []
87+
}
88+
]
89+
},
90+
{
91+
name: "PHP Serialize object",
92+
input: "{\"a\": 10,\"0\": {\"ab\": true}}",
93+
expectedOutput: "a:2:{s:1:\"0\";a:1:{s:2:\"ab\";b:1;}s:1:\"a\";i:10;}",
94+
recipeConfig: [
95+
{
96+
op: "PHP Serialize",
97+
args: []
98+
}
99+
]
100+
},
101+
{
102+
name: "PHP Serialize array",
103+
input: "[1,\"abc\",true,{\"x\":1,\"y\":2}]",
104+
expectedOutput: "a:4:{i:0;i:1;i:1;s:3:\"abc\";i:2;b:1;i:3;a:2:{s:1:\"x\";i:1;s:1:\"y\";i:2;}}",
105+
recipeConfig: [
106+
{
107+
op: "PHP Serialize",
108+
args: []
109+
}
110+
]
111+
}
112+
]);

0 commit comments

Comments
 (0)