Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

Learn how to write readable PHP that is a joy to maintain

Original – by Freek Van der Herten – 8 minute read

I'm proud to announce that our new premium course on writing readable PHP is now available. It's called Writing Readable PHP.

This course contains a collection of bite-size tips (both in written form and videos) that make your code a joy to read for your co-workers and future self. These tips are aimed towards developers who know the basics of PHP and want to improve their craft. As a bonus, you'll learn to use static analysis to ensure that your code is understandable and correct.

Writing Readable PHP has been created by our team and Christoph Rumpel. It contains our combined knowledge on how to write the best PHP possible.

Why we created this course

I still remember coding up my first bits of PHP around the beginning of the century. That makes me sound very old, but let me assure you that I'm still young at heart. My coding was done in Notepad, without any code highlighting or autocompletion. The thing I remember most is the magical feeling that I managed to output some HTML in the browser using PHP.

The code itself, how it was written and structured, didn't feel too important to me. What was important was that the code did what I intended: rendering a page, and that's that. I would write a lot of code but rarely reread it. This became problematic after a while: updating existing projects became cumbersome because I couldn't understand my code.

Nowadays at Spatie, things have vastly changed. Coding is not done on my own but with an entire team. Now I believe that how you accomplish something is as important as the accomplishment itself. This is probably true for most things in life. But let's not get into old-mans-talk-territory and stick to code. How the code is written is at least as important as what the code does. At Spatie, we often say that if you write code that works, you're only halfway there.

You might already have heard that code is read a lot more often than it is written. Through experience, we know that is true.

Over the years, our team and I have learned a lot of patterns, conventions and tips that make code more readable. Most of these things are very small, but using all those tips together makes an incredible difference in code readability. Together with Christoph Rumpel, a fantastic teacher and programmer in his own right, we put all of our knowledge on code readability in a course.

A few examples from the course

The course contains is split in these sections:

  • Visuals
  • Naming
  • Code Structure
  • Modern PHP
  • Static Analysis
  • Laravel

Let's take a look at some of the tips themselves.

Optimize readability for the happy path

One of our favourite techniques for ordering a function's steps is by first handling all edge cases, and keeping the last step in a function for the most important part: the "happy path".

Here's an example where the happy path is handled first. Try to read it: you'll notice that, besides some code overhead, it's also more confusing to understand what this code does from a first read.

// core functionality comes first, special cases handled at the end

public function sendMail(User $user, Mail $mail)
        if ($user->hasSubscription() && $mail->isValid()) {

        if (! $user->hasSubscription()) {
            // throw exception

        if (! $mail->isValid()) {
            // throw exception

Instead, let's first check and handle all edge cases followed by the happy path as the last step of the function. Conveniently, it doesn't need to be wrapped in a condition anymore if all edge cases have already been handled.

// special cases handled first, core functionality comes later
public function sendMail(User $user, Mail $mail)
    if (! $user->hasSubscription()) {
        // throw exception

    if (! $mail->isValid()) {
        // throw exception


How to refactor complex conditionals

The course doesn't only contain written examples, but also high quality 4K videos.

Here's one where we explain how to refactor complex conditionals.

Use the null coalescing operator

In PHP 7.4, the null coalescing assignment (or equal) operator was introduced. This is what it looks like: ??= and as its appearance seems to suggest, it allows you to combine the null coalescing operator from the previous chapter with an assignment.

// traditional way of handling null for an optional argument
function myFunction(MyClass $object = null)
    if (is_null($object)) {
        $object = new MyClass(); // assign a default value
    // ...

You might improve it like this:

// using the null coalescing operator
function myFunction(MyClass $object = null)
    // set $object to its own value, unless it's `null`, 
    // then fall back to a new instance of MyClass
    $object = $object ?? new MyClass();

We're down from three lines of code to set a default value to just one. A significant improvement, but using the null coalescing assignment operator, we can do even better.

// using the null coalescing operator
function myFunction(MyClass $object = null)
    // only set $object to a new instance of MyClass if it's `null`
    $object ??= new MyClass();

Discovering static analysis

PHPStan is a wonderful static analysis tool that can detect many types of errors. In this course, you'll learn how to use this tool to eliminate entire categories of bugs. Let's take a look at a small example.

When using arrays, you can hint at specific keys and their type. Imagine you have an array containing a person’s properties. You could type-hint the properties like this:

* @param array{first_name: string, last_name: string} $personProperties
* return string
function fullName(array $personProperties): string
    // ...

Should we try to use $person[‘non-existing’] then PHPStan would warn us with:

Offset 'non-existing' does not exist on array{first_name: string, last_name: string}.

And here's a very nice bonus: when using this type docblock modern IDEs can provide autocompletion when working with the array keys.


Ain't that great? We just made it far less likely that you'll use a non-existing array key.

Readable Laravel

Most of the content in the course is framework agnostic. Because Christoph and I have a background with Laravel, we thought it would be nice to include a few of our favourite Laravel readability tips as well. Let's take a look at one of them.

In your project, you might need to build up queries conditionally.

$postsQuery = Posts::query();

// adding a condition by hand
if ($latestFirst) {

$posts = $postsQuery->get();

You can clean this up with the when method. The query will only be modified if the first argument is truthy.

use Illuminate\Database\Eloquent\Builder;

$posts = Post::query()
    ->when($latestFirst, fn(Builder $query) => $query->latest())

A new comments package for Laravel

Some of the tips that we give in the course will lead to objectively better code. But there are also tips which are very much opinionated. We decided very early on that we wanted a way that people who follow the course can share their opinion with us and others following the course.

We took a look at existing commenting solutions, and to make a long story short, most of them sucked. Most looked ugly, weren't customisable, or injected an unholy amount of JavaScript.

That's why we've built a custom comments component using Livewire. This component comes with batteries included:

  • beautifully rendered comments
  • nested comments
  • reactions 🥳 👍 🚀
  • syntax highlighting
  • approval flow for admins
  • notifications for new comments for all participants
  • optional read-only mode
  • optional reverse ordering (so the newest comments are displayed first)

You can see and use the component underneath each page of our course.


I'm also already using this comments component on the very blog you are reading. So if you want to see a live example of the component, just go to the bottom of this post.

We've decided that we'll release this component as a separate package. In fact, it will become two packages:

  • spatie/laravel-comments: contains the core logic and notifications. You can use this to quickly build any commenting UI
  • spatie/laravel-comments-livewire: a Livewire powered UI that builds upon spatie/laravel-comments.

The documentation is already available. The component is already very stable, but we want to make it even more customisable than it is now. We aim to release a stable version in the next few weeks.

In closing

Should you decide to pick up Writing Readable PHP, we hope you'll like it!

Our course is a living document: we will add and change things in the content based on feedback on new insights. These tips work well for us, and we hope they will improve your code as well!

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.


Freek avatar

This is the real comment component. Hope you'll like it! You'll find the documentation here. We'll release a stable the package in the next few weeks.

You can use markdown to format things, and backticks to add a code snippet, just like you're used to on GitHub.

public function isPhpAwesome(): bool
   return true;

Feel free to use one of the emoji's below to react, post a reply to this comment, or create a new comment.

🥳 1
Matej Mulej avatar

Is there a similar option for @return, like you showed in @param array{first_name: string, last_name: string}?

Let's say your method returns array of values of different types for example.

$shipment = new Shipment();
$index = 0;
return [$shipment, $index];
// or
// return [
//     'shipment' => $shipment,
//     'index' => $index,
// ];

Can we type hint so types are known when we do the following?

[$shipment, $index] = $this->method();
Freek avatar

Yes, you can use this syntax at @return as well 👍

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.