So this year I inherited an old (as in 3 years) PHP project for maintaining it. Being built on Tigermouse ("Tigermouse is a modern web applications framework for PHP. It relies on AJAX technology, so in pair with being lightweight enables you to build dynamic, responsive and interactive web applications."), it's the worst piece of shit code I've seen in my career. I will probably need to post quite a few topics about it, because putting all the awfullness in one topic would be too much.
So, let's jump right in, shall we? Our primary WTF today: REST and XML are hard..., just to give you an example of the general programming attitude that was in place before we got the project, even though it has relatively little that can be blamed on Tigermouse here.
This project of ours has a "REST" API to manage a few, relatively simple database entities (of the many there are). Nevermind the fact that to authenticate API requests, you have to send your username and password in cleartext as query string parameters (/api/something?user=...&password=...
). Nevermind that there's no SSL anywhere. Nevermind that it's so far away from REST that Roy Fielding would probably start to cry if he ever saw it. Nevermind the fact that it manually constructs XML strings without properly escaping data supplied by unauthenticated users on the Internet. Nevermind that the API is implemented directly inside the database model classes (MyDatabaseModel::dispatchPostRequest($request)
).
But the cake won another aspect of this monster: The code is trying to convert an XML document to an array. To accomplish this, the original developer created an XmlParser class to convert the XML to an array structure. Given the following XML
<?xml version="1.0"?>
<root>
<child/>
</root>
the parser would create this (printed in JSON because it's shorter than print_r()
):
{
"ROOT": {
"ATTRIBUTES": null,
"VALUE": {
"CHILD": {
"ATTRIBUTES": null,
"VALUE": null
}
}
}
}
But if you then add a <child>
tag
<?xml version="1.0"?>
<root>
<child/>
<child/>
</root>
to your document, the structure changes to
{
"ROOT": {
"ATTRIBUTES": null,
"VALUE": {
"CHILD": [
{
"ATTRIBUTES": null,
"VALUE": null
},
{
"ATTRIBUTES": null,
"VALUE": null
}
]
}
}
}
So CHILD
turned into an array of nodes. Fixing this (i.e. creating a stable structure for the surrounding code) was hard apparently, judging from the mountains of debugging statements left behind:
// debug(print_r($this->data,1)); // CORRECT is full of data
// debug(print_r($ds,1)); // CORRECT is full of data
$parser = xml_parser_create($charset);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
$vals = array ();
$index = array ();
xml_parse_into_struct($parser, $this->data, $vals, $index);
xml_parser_free($parser);
// debug(print_r($this->data,1)); // CORRECT is full of data
// debug(print_r($vals,1)); // CORRECT is full of data
$return = $this->getChildren($vals, 1);
//debug(print_r($return,1)); // EEEEEMPPPTTYYYY
// ...snip ...
/*
$tagtwo = "FIELD";
$rtwo[] = array ( "ATTRIBUTES" => "EMAIL", "VALUE" => "hahah@jaddajadda.net" );
debug(print_r($rtwo,1)); // empty... of course....
*/
// debug(print_r($r,1)); // empty... of course....
So instead of creating something proper or just abandon the idea alltogether and stick with using the XML tree, you find this little nugget in ~40 spots in the codebase:
if ($tree['ROOT']['VALUE']['SOMEELEMENT']['VALUE']['ANOTHERELEMENT']['ATTRIBUTES']) {
$tree['ROOT']['VALUE']['SOMEELEMENT']['VALUE']['ANOTHERELEMENT'] = array (
$tree['ROOT']['VALUE']['SOMEELEMENT']['VALUE']['ANOTHERELEMENT']
);
}
But even fixing the codebase like this was too hard, because the original developer simply forgot or was too lazy to fix the dispatchXXXRequest()
methods in the same way. Making the surrounding code puke and throw Exceptions.
Instead, they extended the API documentation to say:
If you are sure that you are sending valid XML but still receive HTTP 500 Internal Server Errors, please include dummy elements like
<someelement dummy="true" />
and try again.
Problem solved.