I recently built a command line daemon in PHP to emulate AWS SQSD for the purposes of testing in Mergebot. As it turns out, one of the benefits of building a large, complex product like Mergebot is that there are pieces of the system that we need to build for the project that might be of use to other developers.
Today, we’re launching that system piece, a free online visual editor for PHP serialized data as serializededitor.com.
In this article, I’ll explain how and why we built this “side” project and how I overcame some of the challenges I faced when building this project.
The Problem
One of the things we realized would be a problem for Mergebot users is that if you had a conflict with data that had been serialized using PHP’s serialize function, trying to edit that serialized data in the “conflict resolution screen” would be a terrible experience. No one wants to edit serialized data manually, and this was guaranteed to be a problem as many parts of WordPress (including plugins and themes) serialize data when it’s stored in the database (widget settings, plugin settings etc.).
So what could we do? We wanted the user experience of merging conflicting data in Mergebot to be as easy as possible (i.e. ideally be able to edit the data visually) and there didn’t seem to be any decent projects that existed in the wild that could help us out with this problem.
The closest thing we found to a solution was the Serialized PHP Editor by SciActive. While this proved that editing serialized data was possible, it was only half of the solution because you still had to manually edit text data by hand (just in a different format). What we really wanted was a “visual” point-and-click editor that would make it super simple to edit serialized data, without having to worry about the format of the data.
So I set about trying to figure out how to make a “serialized editor” possible.
Data Format
The first piece of the puzzle was trying to figure out how to get serialized PHP data into a format that could be consumed and easily recreated. This is not as simple as it sounds and actually took quite a bit of research and time.
PHP’s unserialize function tries to instantiate objects during the process of unserializing the data. This means you can’t just unserialize
the data because if a class is missing, you’ll get a __PHP_Incomplete_Class
object in your data. This was a big issue as the data would be unserialized on the Mergebot app and not in the WordPress install that the data came from, meaning that these classes would definitely not exist.
As a workaround, we tried parsing class names from the raw serialized data and using class_alias to make PHP think the class existed on the Mergebot app. While this strategy worked, we realised that the unserialized data would still need to be converted to another format so that it could be consumed by our editor (e.g. converting it to JSON using json_encode
). However, if we used json_encode
we would lose the structural information that is contained in PHP’s serialized format. We didn’t want to create a whole new JSON-compatible format for PHP’s structure data (although in the end this is kind of what we did).
In the end, I realized that using unserialize
was not going to work and I was going to have to parse the PHP serialized data “as is” to convert it to a format that we could use.
Serialized Parser
At this point I should explain that our “conflict resolution screen” is built using Vue.js (and not React) so this editor would also have to be built using Vue components. I also wanted the whole process (parsing the serialized data, editing the data and re-serializing data) to be completely front-end and not be dependent on any backend processing. So the whole thing is built using JavaScript 😬
The SerializedParser class is responsbile for parsing the PHP serialized data and converting it to JSON. Despite not being able to use PHP’s native json_encode
function, I decided to use the JSON format in the end as it has native compatibility with JavaScript and I realized that I could keep the format of the data fairly simple. For example a serialized string:
s:27:"this is a serialized string";
Could be represented in JSON as the following:
{ "type": "s", "value": "this is a serialized string" }
And an array would be represented like the following:
{
"type": "a", "values": [
{ "type": "s", "value": "this is a serialized string" }
]
}
Another thing I had realized is that objects in serialized data are represented by a string followed by an array of values. So even complex structures in serialized data could be represented in JSON in this relatively simple way.
So now that I had the serialized data in a format that I could use easily, next I needed to build the visual editor that would allow me to edit the data.
The Editor
The SerializedEditor component takes the JSON data generated by the SerializedParser
class and creates a component tree that represents the JSON data. There are two base components that are used to create the entire tree:
- An EditValue component that handles editing individual values
- An Array component that recursively outputs an array of data items
Since the JSON data only has two fundamental “types” (an array of values and single values) it meant that representing the JSON tree in Vue components was fairly straightforward. The components themselves are relatively simple Vue.js and I suggest you take a look at how they work together.
Re-serialize Data
The final piece of the puzzle was to take the Vue component tree and re-serialize the data back into PHP’s serialized data format. I did this by adding a rawOutput
method to the components that returned the state of the component data in the serialized format. For example the EditValue
component method looks like this:
rawOutput() {
return this.item.type + ':' + (this.isString ? this.length + ':"' : '') +
this.editValue + (this.isString ? '"' : '') + (this.item.isObject ? '' : ';');
}
All I then needed to do was iterate through the component tree and call the rawOutput
method on each component and concatenate the strings to create the serialized data (see the generateOutput
method in the SerializedEditor
component). It is a pretty simple recursive operation:
findChildren(component) {
if (component.$children) {
if (component.rawOutput) {
this.allComponents.push(component.rawOutput);
}
component.$children.forEach(child => {
this.findChildren(child);
});
if (component.values) {
// Append closing bracket for array's
this.allComponents.push('}');
}
}
}
Try It Out!
As I hinted at at the beginning of this article, we figured this kind of project might be useful to other developers who are trying to find a nice, simple and visual way to edit PHP serialized data. So today we’re announcing that this project is live at serializededitor.com and in the spirit of collaboration we’re also making the entire project open source at GitHub. We’d love it if you could try it out, share it with your colleagues and let us know what you think!
Hopefully this blog post has given you some insight into how I went about solving this problem and how I overcame some of the challenges involved in building a serialized editor for Mergebot. We’re getting pretty close to launching Mergebot to the public and are super excited to get people merging databases (and hopefully using our serialized editor in the process).
Have you ever had any experience editing PHP serialized data? Have you ever built a side project as part of a bigger project? Would you have done anything differently? Let us know in the comments.