Why I don't like PHP syntax

Read this article in:

Revised and updated 2026-07-26. 

I have made the decision to use PHP as part of my overall stack, along with ruby, javascript, and python. The main reason is that there has been a lot of great software written in PHP, and I want to use it. However, after leaving the PHP world for Ruby, and then coming back to it, I see many annoying little things that bother me in PHP. In this article, I rant about some of them.

But of note, PHP is nowhere near as ugly as typescript. And PHP is an old language, born before the modern internet, and designed for light usage aka "pages". Not for building behemoth monoliths. So at least PHP has this excuse for getting the syntax wrong. Typescript, on the other hand, was allegedly created when the modern internet already existed. There is no excuse for typescript to be as mind-numbingly ugly as it is. It's very sad news, everyone. I'm afraid there will be an entire generation of programmers who think ugly languages are normal. End mini-rant.

And now for the things I don't like about PHP.

ugly loops

If I write:

 foreach ($collection as $block => $region) { ... }

Notice the '=>' token. You would ordinarily see it in dictionary key-value assignment, but here it means the opposite, destructure rather than assignment. Does it make sense to use the same token for it? I think no.

Also notice that when reading the above line, you have to read the middle of it, then go back to reading the beginning. It doesn't read: foreach ($block as $region in $collection) {...} . If I just read the first half and stop, the info is meaningless: foreach ($collection... Maybe php people are used to this abomination, but I am not, and I am hereby pointing out that it is bad syntax.

Compare that to python:

 for key, value in my_dict.items(): print(key, value)

Compare that to ruby:

 collection.map { |key, value| print(key, value) }

Elegant, right? Elegant in each language other than php.

Atrocious namespace separator

What is this?!

 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;

Oh, the backwards slash is... just an ordinary character all by itself. That's stupid, when I saw that I was so offended, I went back to not using php for a year. In the real world, in linux, the backwards slash escapes the next character. The backwards slash is never alone, and the following character is also a special character! You can escape special characters like newlines and dollar signs, you can escape things that don't exist, you can escape half a thing that yet doesn't exist - and it will be future-proof and portable. I'm talking about unicode: you can write \uXXXX and encode a portion of a multi-byte character that hasn't been added to a charset, all using the tremendous power of the backslash.

In a way, the backslash is a meta-meta character, it is more special than a backtick or a dollar sign, it is arguably the most special character of all. And PHP uses it... for what? For nothing, just to separate things. Separate things with a comma.

In fact, in the real world of linux, the backwards slash would escape the next character, and eg `Drupal\Core` would become just `DrupalCore`, since escaping `C` is a no-op and results in the same `C`.

Compare that to ruby:

 class Very::Simple::Namespace {}

compare to python:

 from utils.text.unicode import encode

Again, the only ugly one is php.

Stupefying array syntax

What in the world is this?!

 $person = [
     'name' => 'Alice',
 ];

Square brackets?! But it's not a list. Everyone in the world other than these guys use `[]` to represent lists and `{}` to represent sets/dictionaries/hashes. Don't get me started...

Compare to ruby, python and javascript:

 person = { ## same in ruby and javascript
   name: 'Alice',
 }
 person = { ## old ruby syntax
   'name' => 'Alice',
 }
 person = { ## python
   "name": "Alice",
 }

Again, PHP is the ugly one out.

Semicolons are required?!

Neither in Ruby nor in Javascript are semicolons required. Nor in Python. What do you need a semicolon for? And before you say javascript requires... no it doesn't. And I skip the typescript because it is just chaotic. I remove semicolons from my javascript code as a matter of policy. You know when a statement ends because... I don't even need to explain it, every reasonable modern language just figures it out. Semicolons are a thing of the past and should be optional. Like winding keys for wrist watches: they are no longer needed.

Truth-falsity is wanky

This is not quite a syntax issue, but very relevant to the present discussion. Everyone gets the true/false values wrong! I think ruby got it right, in an elegant way, and everyone else got it wrong. A small excuse is reserved for old languages, where 0 is *maybe* permitted to evaluate to FALSE, since memory space may be tight.

You see, in ruby, everthing is true that is not false. The only false things are nil, and false. Everything else evaluates to true. An error is true, empty string "" is true, and 0 is true. This computes very well, and prevents a lot of pain. Rails introduces convenience methods such as item.present? , item.presence? and item.blank? which allows you to sweepingly determine if an array, set, string, or value is empty in a human sense. But for computing purposes, everything that is not false, should be true.

For example, the Schwab trading platform had to specifically analyze itself for the common case of placing a transaction for net-zero dollars. The Schwab engineers had to sweat a little to avoid throwing an exception in this case. If I buy something for $100 and sell it for $100, the end result should be 0, whereas on the Schwab platform the result was "value not present" or something like that. It is a fundamental fault of the language.

Javascript made the poor decision to evaluate zero to false. So then, when I fill out a form asking me how many criminal convictions I have, and I enter zero, the form will say - oh no no, you must enter a value? Javascript people should nod and say, yes this has been a pain more than once. And by the way, typescript didn't fix it, but only made it worse! Now besides null, zero, false and undefined, typescript introduced void value which by god I hope to never have to find out how it is different from other falsities.

Now in PHP, I just run into this:

$my_config = [ 'type' => 'anything', ... ];
if ($my_config['display']) { ... };

And I get [warning] Undefined array key "display". And now I have to specifically deal with a value that I want to not exist. undefined did not evaluate to false in PHP. You may say oh but you have to define the values, because "programming". But I am specifically not-defining it, I don't want the display! Do I need to talk about everything that I don't want? I don't want a cup of coffee right now, I don't want bread, I don't want to replace a view with an edit, I don't want a dolphin in my codebase... Hopefully you can see my point. I shouldn't need to define something that is *not* there.

So PHP got it wrong in that 0 is not true, and that undefined is not false.

~ * ~ * ~ * ~

I'll update the list once I see or remember other annoying things. But for now - I hope you can see how people have strong language preferences. And this was just simple syntax, not reflection, not standard library or other considerations. I think ruby is an infinitely more elegant and pleasureful language, and it is upsetting that ruby is not getting enough use from the world's programmers.

 

Please login to post comments: