… coders get hurt.
Today I was doing some maintenance on one of the applications I am working on. This particular application requires PEAR::DB_Table to talk to the database. I use it because the data access is rather straight forward and one of it's features is that it can automatically generate forms from a database using PEAR::HTML_QuickForms so it has a rich set of GUI elements; one for each datatype. Internally PEAR::DB_Table converts the form data (which arrives as strings) to the correct datatype to insert the data into the database using PEAR::DB or PEAR::MDB2.
Most of the time, this process is runs like a hot knife though butter, the API is straightforward and easy to use. It is extensible and it just works. Well, at least until today. It literally took me hours to find the source of this little bug. It started with some sql errors showing up because some malformed data was inserted into the query. Floating point values appeared with a comma delimiter instead of the standard dot. Instantly I figured this had something to do with the use of
number_format() and I hunted down all occurrences of the function. To no avail.
After staring at my code some more, quite frustrated already, for a couple of more minutes. I decided to blame it on PEAR::DB_Table and find out what it did wrong. I came across all sorts of functions and none of them seemed to do anything strange with floating point numbers, no number_format() or 'smart' str_replace functions to be found. All looked fine and after having spit though PEAR::DB_Common I finally found the problem. Hidden somewhere in PEAR::DB_Table there it was, a call to settype.
The problem? PHP's floating point representation is based on the current locale, as vaguely documented on the
setlocale() page. And it just happened to be that the locale I'm using uses a comma delimiter instead of the (standard) period; this would hardly call for problems on its own, however it is deadly when you are dynamically generating source code (such as sql). A rather awkward solution to this is to actually use number_format() to get around this problem.
<?php
// Code used to reproduce:
setlocale(LC_NUMERIC,
'nld',
'dutch',
'nl_NL'),
"\n";
echo 12.
5,
"\n";
// output: 12,5 ?>
Although PHP is very well suited for people who are learning to code, these things can be really frustrating when you are actually trying to accomplish something because a lot of it's language features are dependant on the environment it is living in. That means that writing portable applications just became very hard and bugs in your application can be difficult to find. I've read that PHP6 will change some of that behaviour, thankfully, because I think programming languages should be completely transparant to it's users. Datatypes should not be dependant on the processor architecture or locale settings; to be portable the code has to run the same everywhere.