What I hate about PHP

This is what I really hate about PHP:

pilif@galadriel ~ % cat test.php
<?
if (10 == '10ABC')
    echo "Gnegg!n";
?>
pilif@galadriel ~ % php test.php
Gnegg!

This is the reason for a pretty serious bug in my current i’m-loving-doing-that-as-it’s-the-greatest-ever-project

What happens is that PHP implicitly converts 10ABC to an integer (yielding 10) and then making an integer comparison.

In my oppinion, this is wrong as inplicitely converting a string to an integer can cause information to be lost. Would PHP have converted 10 to ’10’, the comparison would have worked like one expects because converting an intger to a string works without losing information.

Then again, integer-conversions are more accurate than string conversions, so I can understand PHP’s way. What I cannot understand is that a non-integer string is converted to something else than 0 or nothing (while causing a runtime-error). The comparison in my example should never have evaluated to a true value (which happened, because intval('10abc') == 10!

And converting to string if one argument of a comparison is a string is not the holy grail either – problems with locale-specific decimal points come to mind (is it . or ,?).

So perls idea of using a dedicated string comparison operator may not have been a bad idea after all…

Apache 2

There was this discussion recently about whether Apache 2.0 should be recommended by the PHP guys or not.

While I find their warning a bit too harsh, I for myself still cannot run Apache 2 – though I’d really like to. So maybe it’s time to add my two cents:

Last march, I was going to newly set up our productive server. As the apache guys keep telling that Apache 2.0 is production ready, I first went with the new version of course. Here’s what did not work and finally forced me to go back to 1.3: It’s not about PHP at all: The two extensions I’m depending on (MySQL and PostgreSQL) are available in a threadsafe edition, so even one of the threaded MPMs would have worked. What killed my intentions was mod_perl.

Back then, when the comment-spam problem was not that a big one for me, I have been running gnegg.ch in a mod_perl environement which at that time was not setupable with Apache 2: mod_perl itself had an even bigger warning about not working well than PHP still has. And additionally, they’ve changed their API, so even if I’d been able to get it to work, there would have been no guarantees of getting MT to work with that new api.

Anyway: I’ve been willing to try it out, but libapreq, required by MT when running in mod_perl, was only available as an early preview too (still isn’t nowhere near production ready). My tries in installing it anyway lead to a flurry of SIGSEGVs in Apache when using MT. Judging from the Gentoo bugtracker this has not gotten better yet.

One of the strongest selling-points for Apache isn’t PHP. It’s mod_perl. And currently, it’s mod_perl that should have this big warning on its webpage. Mod_perl and not PHP (which works nicely under Apache 2 in an internal developement system).

And even when mod_perl gets fixed: As they have changed the API, many existing (and not longer maintained) packages using mod_perl (like Apache::MP3 for example) will possibly stop working after the switch to Apache 2.

As soon as the first guy comes here and posts that he/she’s gotten MT to work under mod_perl on Apache 2, I’m going to reconsider the switch. Not a second earlier.

Horde 3.0

Today Horde 3.0 along with some applications using it, the most noteworthy being IMP 4.0, has been released.

For me, horde has a long history of being a pain in the ass to install and extend. While installing the first versions has been quite easy (but not possible for me back then as I did not have access to my own server and the environement of our shared hoster did not have all the extensions needed – let alone shell access), it grew quite complicated with 3.0 onwards.

My main problem has been and is that Horde is not really a framework for application developement, but a frontend-container. It’s not possible to just install IMP. You’re always installing a kind-of groupware-application (Horde) and only them the webmail component

If you don’t do it right, you actually force your users to login in twice when checking their email (once in horde, once in IMP)

As always, I really had to take a look at those new releases.

As the horde main server is quite busy currently, I’ve downloaded from the mirror in the netherlands – the others where either not reachable or not current.

After downloading the horde framework, satisfying the very long list of dependencies took some time. Especially tricky was the fileinfo PECL-extension but this was because of a problem with my local PHP installation. Glad I found out now and could fix it

Then came the configuration. What a nicely done web interface! Unfortunately, I just managed to lock myself out (I chose “IMAP Server” as authentication source not knowing that this only works after IMP is installed and IMP cannot be installed without a working horde installation…)

After those things where setteled, I came to the installation of IMP. Easy procedure here – after getting used to with the framework itself before.

Then I’ve configured horde to use IMP as authentication source which did not work at first but after copying over the backup configuration file and trying again, it finally worked (don’t ask me what I did the second time).

My next problem was the preset settings for my users: Per default, it’s using a 12 hours time format, Arabia as location and somewhere in Africa for the time zone.

As I cannot ask my (possible) users to change those preferences, I looked for a way to do that and while doing that I began to understand how the Horde configuration system works.

Now, I’m quite impressed about how they are doing this: it’s generic, it’s configurable and every single feature can be locked down for the end users. Very nice.

Just make your configuration changes in config/prefs.php. If you need a list of possible values, either read the source, or easier: Just look at the HTML source of the preferences-screens.

If I had a whish for the next release: Provide a way for the administrator changing those settings via the Web-Frontend.

While I first just installed IMP, which worked flawslessly out-of-the box, I ventured further and installed kronolith, turba, nag and fiinally even chora. Additionally, I configured Horde to give access to chora only to me. Comfortable. Even more impressive, when I recall that the whole user-management is done via the XAMS environement (by using IMP to authenicate the users).

All in all, I still would whish to hide away horde and just install IMP (with a small, simple integrated addressbook), but as a) IMP really is the best (PHP-based – I don’t know no others) webmailer out there and as the other applications work really nicely (even with PHP5, though it’s not officially supported), I can live with that limitation.

Now, I have two tasks ahead of me:

  1. Provide support for changing the XAMS account password from within the web interface. This will be a great opportunity to learn how the preferences system really works.
  2. Teach Ingo how to create Exim-Filters as this is the filtering system that could most easily be integrated into XAMS. When I designed the initial draft of XAMS (then still called pmail), I took great pride that the mail delivery does not cause a non MTA-process to be forked an I want to keep it that way. It saves resources under high mail load.

After the christmas days, I certainly will know what the new Horde/IMP is made of. From an Administrators/Users perspective, it’s a great release.

Thank you guys!

Wasted hours

Today I wasted three hours finding a bug in my code: A server-side plugin of our PopScan-Server recently stopped working. Looking at the code, I’ve quickly seen that some queries to the customers MS SQL-Server seemed to fail.

Nothing helped. I did not even get a message what’s going wrong. mssql_query() just returned false.

In the end, I created a small, reproducible testcase and reported a bug in PHP. And guess what: It’s already fixed.

This seems to have been introduced between 5.0.2 and 5.0.3 which is bad as I had to update because of the recent security problems. I really find it questionable that a security-update can introduce non-related bugs. But that’s live. I’m happy for now.

This is the first time in the last 6 years I’ve been working with PHP so far that I’ve been hit by a bug in PHP itself in a critical situation. This also is the reason why I wasted three hours searching for the bug in my code instead of going after PHP. I just thrusted PHP more than myself.

Zwei Affichen

This is only for my fellow readers from the german part of switzerland. I’m presenting it without further comments as those knowing about the two newspapers and capable of understanding german will certainly get my point.

I took the pictures with my cellphone, so the quality is kind of bad which is why I do not provide an enlarged version

Tagesanzeiger NZZ

Productive with Delphi 2005

Yesterday and today, I finally had the opportunity to do some real work on PopScan with Delphi 2005. Here’s what I really like besides the obvious:

  • Those .bdsproj-Files are incredibly useful. They replace the old DSK and DOF-Files, have a convenient XML-format and are the new project file, you open with in Delphi. This is very nice as the old project file (.dpr) is actually program code and does not contain any project metadata. This is what those .dof and .dsk-Files where used for, but I never understood which setting is in which and the format has not been XML either. So this consolidation really is convinient.
  • The history-feature really saved my day. With me hitting Ctrl-S on nearly every line I write, the older .~pas-approach wasn’t very useful and CVS was no help either because I don’t commit as often as something could go wrong in the code.
  • The new exception catching-dialog of the debugger is really nice. I like this “Dont halt on this exception again”-Checkbox.
  • While it makes the application significantly slower under the debugger, the new “event log” is great.
  • Speaking of debugging: The “Local Variables”-Window is great too.
  • Delphi now distinguishes between a “Default Layout” and a “Debug Layout”. You can configure both of them as you like and Delphi automatically switches between them. This is much more intuitive than before.
  • Maybe I’m the only person on earth, but I like the single-window-approch: It’s much cleaner than before. No more tons of clutter on the screen and the important screens are always wisible. No more Ctrl-Alt-F11 either.

Additionally, I don’t have as much speed problems as others seem to have: While starting the IDE takes it’s time, working with Delphi when it’s open goes quite smoothly.

My only problem is opening the form designer. This definitely takes too long, but not long enough for me to switch back to the undocked layout.

Memory usage is of no concern to me. I have 1 GB of RAM and even after a day of using delphi, my Thinkpad remains responsive even though not only delphi, but also eclipse, Zend Studio, Firefox and many other programs are running. For me the figure the task manager tells me is not nearly as important as the responsiveness. If delphi uses 500 MB of RAM, fine – as long as my PC stays responsive.

All in all, I really like this new Delphi and I already have uninstalled D7 (thus breaking FeedDaemon).

Tales of Symphonia

Now that the project I’m currently working on for which I didn’t really have much time to complete it and which I insisted in doing cleanly despite the time constraints (beleive me: It’s worth it. Read about that later) is coming along very nicely, I actually had some time to do a little gameing yesterday.

About two weeks ago, I bought Tales of Symphonia for my gamecube, but only yesterday, I played it for the first time (while still waiting for Mario 64 to arrive for my DS I’ve imported and actually got last week). Read about my more-than-plesant experience:

First of all, I actually could buy a legal european version. Relying on grey import was – for once – not necessary despite Tales of Symponia (just “tales” from now on) being quite a hardcode RPG. A really big THANK YOU! for that, Nintendo.

Additionally, while I would have preferred playing it in english, the german translation is really good (completly unlike the miserable translation of Pokémon, for example) and thankfully, the voice actors where not synchronized and the english actors did a very good job on this one.

One thing is stupid though: You cannot turn off the german subtitles and they do not vanish automatically. So it’s necessary for me to hit the A-button in just the right time not to create unnatural sounding cutoff sentences. This was a problem in the first 15 minutes. After that I got quite used to it, maybe also because the german translation really is good (I’d translate most of the sentences like they did).

The next thing I did not like at first was the story: First, you have this “Wake the goddess to save the world by unsealing four seals” which sound kind of silly for a hardcore RPG. And then there are the two other main themes: “Girl on a pilgrimage to save the world” and “Boy brings destruction to his own village because of an accident and gets banished for that. His first station on the journey is a desert”.

Both of those themes should sound familiar to you, the first one being a FFX-ripoff, the second one being from the best RPG of all-time, Xenogears.

Fortunately, this feeling of “seen-that” quickly begins to wear off after about two hours where the party crosses the sea and lifes (hopefully) through the Governour Dorr-sidequest. Now, that’s something new (and great too).

As I’m just about ending said quest, I don’t know anything further to say about the story, but I’ve read great things about it.

I really like the battle-system. It’s a bit like “Star Ocean”, fast-paced and doable none-the-less. In the desert just at the beginning after being abducted by those maybe-desians (the enemy race opressing the humans, strangely equipped with technology well beyond that of the humans), I was hopelessly under-leveled: Those visible enemies on the world map invite you to skip instead of fight them. In the end, I got around, but it was not easy there.

On a side-note: Speaking of advanced technology: Why the heck does Raine seem to know all that stuff? What is it about her? If she has something to hide it’s much better done than Citan in Xenogears where this is clear from the beginning. Besides: I really like her character. She is very likeable.

Another thing I really, really like is the graphics: I love this cell-shading technology – especially if it’s done as well as in tales. It’s like watching an animee – just interactive.

On and talking about “interactive”: In contrast to what I had to rant about in Xenosaga, in tales, the balance between interactive and non-interactive sequences is done very well. It’s never boring and the story is always developping. Very nice.

All in all, tales certainly is the best I’ve seen RPG-wise on the gamecube and it even matches some of the better-known Squaresoft titles. I really hope, the story continues as it is now and does not fall back to re-telling things already told by other games.

If you have a cube and are longing to good RPGs on it, go and buy tales. You will not regret it.

So, now I’m just going to recompile and upload my little Java-Applet and then I’m off home to play another round of tales…

ALTER TABLE in PostgreSQL 8.0

I’ve just discovered my new favourite feature of the upcoming PostgreSQL 8.0: Let’s say, you have forgotten a column when creating the schema of a table. Let’s also say there already exist foreign kays referencing this table, so dropping and recreating it with the updated schema from your text-editor won’t work (or force you to recreate all other tables too).

So, you need alter table

Here’s what Postgres < 8 needs to add a column cdate which must be not null and have a default-value of current_timestamp:

alter table extart_prods add cdate timestamp;
update extart_prods set cdate = current_timestamp;
alter table extart_prods alter column cdate set not null;
alter table extart_prods alter column cdate set default current_timestamp;

And here’s what it takes to do it in PostgreSQL 8:

alter table extart_prods add cdate timestamp not null
    default current_timestamp;

When typing this into psql, you’re so much faster. This is actually the only feature I really missed when going from MySQL to PostgreSQL for all bigger work

Oh and did I mention that in Postgres 8 (currently running Beta 4) the statement is executed noticably faster than in Postgres 7.4 (though this doesn’t really matter – you should not be altering production tables anyway)

Internet Explorer, File Downloads, PHP

Have you ever tried sending a file to Internet Explorer, for which an internal displaying plugin is installed? Take a .CSV-File for example (or a PDF for that matter).

If so, then maybe you have noticed that IE in some versions just displays an error-message about not being able to find the file just downloaded whenever you have a call to session_start() in your script.

The problem is with the Headers PHPs session management sends to the browser: It disallows any cahing and tells that the document expired somewhere around my year of birth (1981). It seems like IE takes that literaly and really does not cache the doument, but then naturally is unable to forward it to the plugin (or activex-control or whatever).

Fortunately, you may change PHPs default headers by just emitting some additional header()-calls:

    header('Content-Type:  application/csv');
    header('Pragma: cache');
    header('Cache-Control: public, must-revalidate, max-age=0');
    header('Connection: close');
    header('Expires: '.date('r', time()+60*60));
    header('Last-Modified: '.date('r', time()));

A short explanation of the headers sent:

  1. The content-type tells the browser that there’s a CSV file coming
  2. Pragma is an old HTTP/1.0-Header. This one allows caching of the resource
  3. Cache-Control is the new HTTP/1.1 header to replace Pragma. “public” means: Public proxies may cache the document (private would also work and would mean: Cache in the Browsers cache). must-revalidate advises proxy servers (and browsers) to check if the resource is modified whenever the document is older than max_age seconds.
  4. The connection-header tells the server and browser what to do with the connection when the resource has been transmitted. The old HTTP/1.0 behaviour is close. keep-alive would be the newer behaviour. I’m not sure whether this really is necessary here, but with this header, it definitely works.
  5. The Expires-Header tells the browser when the document is going to expire. PHP default this to somewhere in 1981 and I think this is what causes the problem for IE. I set it to one hour in the future. If it were possible to just turn off those default-headers, I would simply send no Expires-header at all.
  6. Last-Modified tells the browser when the resource was last modified. I could actually get a timestamp of the underlying data representation and output that so the browser would not have to redownload the resource when the data has not changed, but it’s changing that often that this optimization is not worth the trouble, so I’m telling it just changed.

I have confirmation that this solves the problems some clients where expecting before. Very nice.