More fun with PHP!

We all know dates are hard, which is why we let libraries and built-in language features handle them. Right?

Well, turns out you can’t even trust those. I was just writing the following code, that needed to determine the first day of the next month for me. Using PHP’s strtotime function, this oughtta be easy, right?

$firstDateOfNextMonth = date('Y-m-01', strtotime('+1 month'));

Only, today is August 31st, so PHP actually turned this into 2017-10-01, because obviously that is correct. And yes, my timezone settings are local – var_dump(date('Y-m-d H:i:s')) gave me 2017-08-31 17:28:22.

For comparison, PostgreSQL does what I expect:

# select NOW()+'1 month'::interval;
           ?column?            
-------------------------------
 2017-09-30 17:31:25.025405+02
(1 row)

Sighs time to file a bug I guess…

WTF PHP?

PHP has come a long way in recent years, and though it’s still not by far the most elegant language ever PHP5.3+ at least looks like an actual programming language.

But today I got bitten by something that totally flabbergasted me. Consider the following code:


<?php

$foo = new Foo;

Obviously, without any autoloading mechanism this gives a fatal error as class Foo is undefined. Now, modern versions of PHP have a handy feature where you can call ClassName::class and get the actual classname. This is handy for when it’s imported from some long namespace like so:


<?php

use Foo\Bar\Baz\Fizz\Buzz;

echo Buzz::class; // 'Foo\Bar\Baz\Fizz\Buzz'

So what do you expect happens when you do this to a non-existing class? After all, ::class is some sort of virtual constant on an actual class definition, right?

Wrong.

<?php

echo Foo::class; // 'Foo'
$foo = new Foo; // fatal error, class does not exist

Ehm…  That’s just… Wow.

The only reason I can think of why this works as it does is that the ::class is evaluated at compile time, while the actual instantiation is triggered at run time (thus causing autoloading to kick in).

To add to the party, I ran into this doing a Laravel project, and the autoloader swallows errors in the autoloaded class and simply says “sorry, no such class” (there was actually a typo in the loaded class, which I’d love to have been notified about before wasting an hour). I still need to find out what exactly is going on there, but I’m pretty sure it’s not Composer – I’ve never run into this behaviour on non-Laravel projects. (Also, the error occured in a Laravel “Command” which eagerly swallows errors too, because fuck you programmers debugging stuff.)

Tl;dr: just complain loudly if stuff fails. It’s fine. If I want to handle live errors more gracefully, I’ll just try/catch index.php or whatever myself based on some environment variable thank you very much.

The poor man’s PHP daemon

We have this project lying around from a while back that’s based on PHP/AngularJS and also sports a socket.io server for (among other things) a real time chat. Pretty nifty (no, we didn’t do the design :)). The socket part is powered by a NodeJS process, and since we didn’t feel like (nor did the client have budget for) rewriting all our PHP code to Javascript, we used dNode-PHP (well, actually a fork with a few small project-specific adjustments) to let the Javascript code in the NodeJS process communicate with our existing PHP libraries. So now we had two processes which is suboptimal but worked at the time.

As a poor man’s solution (time pressure, limited budget, etc.) these processes were simply kept running in a permanent screen on the server. In theory, that worked well enough for then – the idea was that once the client found new budget, we’d finally properly daemonize them. And our server doesn’t reboot that often anyway, so remembering to restart the screen and processes on those occasions wasn’t a big deal.

Of course, that day never came.

There was however one annoying issue: PHP’s database resource would go stale after a while. According to the docs it should automatically reconnect, only it didn’t. This brought the need to manually restart those processes every once in a while (we put the MySQL timeout to a week to alleviate the burden, but the exact moment was random depending on the last moment of activity. Of course, one could argue that a site that’s completely inactive for >7 days regularly isn’t worth the effort, but a) that wasn’t our problem and b) the client was still working on his marketing plan. Fair enough).

Today I got fed up with it, had an hour to spare, and the marketing plan was ready as well. Time to bite the bullet; here’s what I came up with.

1. The NodeJS process

That part was easy; essentially I followed the instructions here. We don’t use Ubuntu but rather Debian, but it should be similar on all *nix systems.

2. The dNode-PHP process

This is where it got interesting, and which had been the actual bullet I’d been putting off biting. PHP isn’t very well suited to run as a daemon. It’s possible, but that doesn’t mean it’s desirable, in the same sense as writing out all your CSS in <script>document.write('<style>...<' + '/style>')</script> tags is only a theoretical option. But in this case it was still better than duplicating code in Javascript.

Now, there are ways to turn a PHP script into an actual daemon. It was still overkill for our purposes, so I simply went with a cronjob that killed any existing process and restarts it every hour. If the client ever needs an actual daemon, we’ll get to that then 🙂

Yes, this “daemon” is a beggar, but as they say: beggars can’t be choosers…