This bit me quite a while ago as well.
This fuckup in PHP's lexer is the reason why I stopped caching stuff via var_export()
and writing it to .php
files. You get bad luck and your data is exactly 4k in size and bam, project dead.
This bit me quite a while ago as well.
This fuckup in PHP's lexer is the reason why I stopped caching stuff via var_export()
and writing it to .php
files. You get bad luck and your data is exactly 4k in size and bam, project dead.
Even if there was some development still going on, I'd doubt that the original developer would do the necessary things (i.e. throw everything away, apologize and start over). ;-)
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.
Just for clarity, I'm working in Germany, so switching between dot and comma for decimal separators (partially via locales, of course) is a common thing for me. "Exotic" would be Turkish with that weird lowercase i business. ^.^
If I could, I'd replace all usages of setlocale() and related things with the intl extension, but good luck a) finding web hosters that have it enabled and b) making sure your entire codebase relies on intl.
Nope. Mostly because setlocale() works like shit on Windows ;-)
What should I expect from "exotic" locales? What are those?
I've never had that happen to me with json_encode(), but I made it a habit to always use sprintf('%.F', ...)
(note the capital F) when turning floats into strings that are meant to be processed in any way, usually with a length parameter (e.g. %.2F
).
Well if you find a way to "break out" of a json_encode'd string, please tell me how ;-) But please report it secretly to bugs.php.net before that
I don't consider using json_encode() as "misuse" here. I want to represent something as JavaScript, so I convert it in JS's ON form. Simple as that. Takes care of accidentally having true/false/null etc. and is certainly a lot better than using addslashes() or htmlspecialchars().
In this case, though, having passwords match is just something you do against accidental typos. So doing this validation on the client-side (where 99.9% of people have JS enabled nowadays) is enough IMHO. If HTML5 would allow me to check if two fields match, that would be enough for me.
Of course, enforcing password complexity is something you obviously [primarily] do on the server.
It also doesn't have a UnicornException
. So what? Do you really except the SPL to contain exceptions for all your possible specific problems? And then people bitch about "Ugh, look at all those useless exception classes in PHP. Why did they put a DateIsInThePastException
in the PHP core?!"
Throw a RuntimeException
and just inherit your own one from that one and be done. This also gives you the wonderful opportunity to namespace your exception so people can have better catch blocks.
At least pick a worthy battle when bitching about PHP.
+1 for suggesting json_encode to take of JS string quoting oddities.