Is PHP still relevant in 2021?

As one of the poster childs for bad programming languages, PHP has always been in the top 5 “worst programming languages”; however, as of 2021, is that still a thing?

A Brief history of PHP

PHP was developed by Rasmus Lerdorf in 1994. Lerdorf developed a bunch of scripts to track the visits to his online resume, and named them as “Personal Home Page Tools”, which then evolved into being called as “PHP Tools”. He kept adding more tools to this suite, and at some point pulled off a rewrite of the tools, including added functionality for database interactions and more, turning it into a more complete framework. From that point on, the tools have evolved into a more complex primitives and kept gaining more users after it is open-sourced in 1995. A more detailed history of the language can be found in the official PHP website.

As of now, the latest version of the PHP language is 8.0.

What is wrong with PHP?

The language has had a target on its back for years now, and people are rightfully calling out the bad taste they have with the language, especially with the older versions. The language has been developed with the intention of being a templating language, rather than being a full-blown programming language; therefore, there are some downsides with it that made it especially harder to maintain larger apps.

Weak Typing

One part of the language that I personally dislike is the weak typing that allows combining different types and casts them implicitly. Consider the following example:

echo "1" + 3;
echo 1 + "3";
echo "1" + "3";

The results of all these operations are 4, which means the language casts the numbers in the string to integers in the context of the addition operator. This might be desirable in some cases or it might save a few lines of code here and there, but the larger a project gets, the harder it becomes to maintain it.

The more recent versions of the language have started introducing warnings for these kinds of weird and invalid operations, which means that they are either deprecated or already on their way to be deprecated.

Lack of Namespaces

The support for namespaces has been introduced in PHP by the version 5.3, which means that all the older projects have had to build their own kind of namespacing, which usually relied on adding namespaces to the class and method names, requiring absurdly long names everywhere. For projects that were developed with the prior versions, it is very common to see classes named like Payments_Provider_ProcessorProvider_SomeExternalServiceProvider whereas it could have been named like SomeExternalServiceProvider simply. This results in very verbose code in most of the cases, and it makes it harder to read and skim through the code.

The more recent versions of the language doesn’t have this problem though.

Inconsistent Standard Library Functions

I am not saying the standard library of the language is bad, but one could argue that it could have been better. To be fair, the language has been improving quite a lot, but the early versions of the standard library, which is already being used, referenced and supported due to backwards compatibility results, were lacking consistency. Although a small disturbance, this meant that many of the standard library functions had different naming conventions, argument names and ordering, making it harder to assume the defaults and the behavior.

Here are some naming inconsistencies with the string methods:

Three different functions, one with a str prefix, another with str_ prefix, and the third with no prefix. The $string argument is the first argument for str_split, but the second one for the explode one. You can check out all the string methods in the documentation, and each of these patterns have many functions following similar patterns, meaning that there is not much of a consistency with these functions.

Superglobals

More of personal choice, but I hate the use of the globals, and consequently superglobals. Especially if you run into some home-baked old projects, it is highly likely that you’ll run into the famous variables like $_SERVER or $_REQUEST. Don’t get me wrong, these are very helpful sometimes and will need to be used eventually; however, encapsulating these into reusable classes should be done as one of the first steps in order to be able to use these values safely. If not, touching these values or doing any change in a slightly larger project gets a very complicated experience where there are many hidden dependencies on these values.

What is good with PHP?

Even though it had left a bad taste in many people’s mouth, the language itself has been improving quite a lot in the last few years. With the release of PHP 7, the language has gone through a modernization process where many nice features were introduced to the language basics, the speed was improved, and the usability has increased quite a lot.

Type-hints

This is one of my favorite ways of modernizing legacy PHP code: using non-enforced type-hints that handle type casting as well as providing documentation for the code. Check out the following simple function:

function isValueSomething($value) {}

If you include the type hints, it becomes something like this:

function isValueSomething(string $value): bool {}

Just by looking at the signature, we are able to tell it expects a string value, and it will return a boolean result. One could claim that the naming convention could have been useful here as well, but these type-hints reassure that the values will be of those types, as well as giving the IDE a lot of power for auto-complete and static analysis with warnings and stuff.

Since PHP 7.4, PHP allows defining typed properties for classes as well:

class Person {
    public string $firstName;

    public string $lastName; 

    public int $age;

    public ?string $job;
}

This means your Person objects will have string first and last names, an integer age, and a nullable string value for the job. Being able to define this becomes very useful the more classes you have.

Syntax Improvements

PHP now has bunch of syntactical improvements:

  • Arrow functions: fn ($x, $y) => $x + $y;
  • Null coalescing operator: $value = $array['key'] ?? 'default value';
  • Null coalescing assignment: return $cache['key'] ??= computeSomeValue('key');
  • Array spreading: $first = ['a', 'b']; $second = ['c', 'd']; $final = [...$first, ...$second];
  • Named arguments: array_fill(start_index: 0, num: 100, value: 50);
  • Numeric literal separator: 299_792_458

In addition to these syntactical improvements, it also includes stuff for more complex improvements.

Constructor Promotion

Look at the following Person class:

class Person {
    private string $firstName;

    private string $lastName; 

    protected int $age;

    public ?string $job;

    public function __construct(
        string $firstName,
        string $lastName,
        int $age,
        ?string $job
    ){
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        $this->age = $age;
        $this->job = $job;
    }
}

Instead of having this unnecessarily verbose code, PHP 8 supports writing the following code:

class Person {
    public function __construct(
        private string $firstName,
        private string $lastName,
        protected int $age,
        public ?string $job
    ){}
}

Nullsafe Operator

This is something that had existed in some other languages like Javascript but PHP didn’t have the support for this. Take a look at the following code which I grabbed from the PHP docs:

if (is_null($repository)) {
    $result = null;
} else {
    $user = $repository->getUser(5);
    if (is_null($user)) {
        $result = null;
    } else {
        $result = $user->name;
    }
}

This was how the logic would be written with the older PHP versions with respect to null-checks. The new nullsafe operator allows converting this to simply:

$result = $repository?->getUser(5)?->name;

Isn’t it gorgeous?

Union Types

Even though this is a less favorite feature of mine, it is still valuable for the cases where there is already multiple possible types and we don’t type-hint anything. Union types simply allow defining multiple types for a value as options. Thanks to the union types, the following code becomes valid:

function doSomething(int|string $value): bool|array {}

Usually having multiple return types indicate an opportunity for improvement, but previous versions of PHP didn’t allow us to define types for cases like these at all, so having this is still an improvement.

Performance

I don’t have any hard numbers compared to other languages, but PHP has improved significantly over time compared to the previous versions. In addition to the jump PHP 7 brought over PHP 5.6, all the consecutive releases have brought several percent-point improvements at least and the trend is continuing. Some benchmarks done by Phoronix show that the latest PHP 8 is more than 3x faster than PHP 5.6. There are more detailed tests in the original posts, make sure to give it a look.

PHP Benchmark 1

In addition to those benchmarks, Kinsta has also conducted some real-world benchmarks with tools like Wordpress, full article here. Here’s the result for Wordpress 5.3:

PHP Benchmark 2

The numeric results they have shared are:

  • WordPress 5.3 PHP 5.6 benchmark: 97.71 req/sec
  • WordPress 5.3 PHP 7.0 benchmark results: 256.81 req/sec
  • WordPress 5.3 PHP 7.1 benchmark results: 256.99 req/sec
  • WordPress 5.3 PHP 7.2 benchmark results: 273.07 req/sec
  • WordPress 5.3 PHP 7.3 benchmark results: 305.59 req/sec
  • WordPress 5.3 PHP 7.4 benchmark results: 313.42 req/sec

These benchmarks do not include PHP 8 yet, but the 7.4 is capable of handling 3x requests of 5.6, which is a pretty significant improvement.

Conclusion

Overall, PHP has improved quite a lot over the last few years, and it has become a joy to use for me. I am working with Golang, PHP and Python professionally, and I have the most experience with PHP, so I am highly biased here; however, PHP is the one that I believe sits in the sweet spot between flexibility and maintainability. It has all the flexibility to do crazy stuff, it has a flexible typing system that allows improving legacy code gradually, it is fast enough for many usecases and still improving, and it has an incredible open-source community behind.

For those who had a bad experience with PHP with the older versions, I suggest giving it another shot. There is a chance that you might still not like it, but I believe many of the older takes about the language are subject to change with a fair view of the latest version of it.

Try SaaS Starter Kit

Subscriptions, 3DS and SCA payments, Docker, Kubernetes, CI/CD, and more.

Up to 75% Discount for Early Access period