Are you...
Stop the madness! Let’s learn how to write readable PHP in this hands-on course.
VAT will be calculated during checkout by Paddle.
We support Purchasing Power Parity.
Buy Writing Readable PHP combined with other courses
and get an extra discount
Buy together with this ebook on cutting edge tactics in PHP 8, accompanied by videos and practical examples.
front-line-php.com
Buy together with this video course to get started with Pest and PHPUnit by Brent Roose & Freek Van der Herten.
testing-laravel.com
In a previous part we've mentioned that grouping code in paragraphs is good for readability. There's another advantage: it visualises the steps a function takes to get to a certain results. It makes sense to order those steps in a way that's natural to you, the programmer.
Our favorite technique of ordering a function's steps is by first handling all edge cases, and keep 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()) {
$mail->send();
}
if (! $user->hasSubscription()) {
// throw exception
}
if (! $mail->isValid()) {
// throw exception
}
}
So 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
}
$mail->send();
}
Consider adding the unit to the name whenever you work with something measurable.
// bad: we don't know what that 100 represents
$averageTime = 100;
// good: we now know that it is 100ms
$averageTimeInMs = 100;
Another way of dealing with this is by creating dedicated objects. Imagine that you need to work with a percentage. Which of these is correct?
$percentage = 0.5;
$percentage = 50;
You can’t tell what your application expects. Let’s now use an object with a static constructor, one for each possibility.
class Percentage
{
public static function fromInt(int $percentage): self
{
return new self($percentage);
}
public static function fromFloat(float $percentage): self
{
return new self($percentage * 100);
}
private function __construct(
public int $value;
) {};
}
Using a `Percentage` class clarifies that an integer is expected.
$percentage = Percentage::fromInt(50);
In PHP 7.4 the null coalescing assigment (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 could 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();
}
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 with 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 an non-existing array key.
In your project, you might need to build up queries conditionally.
$postsQuery = Posts::query();
// adding a condition by hand
if ($latestFirst) {
$postsQuery->latest();
}
$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())
->get();
"Writing Readable PHP" will give you some amazing tips on how to write clean and modern PHP applications. It's full of tips I learned myself over the years as a programmer and it's great to see them all come together in this book.
Freek and Christoph bring a ton of experience to the table. They make sure you have all the tools you need to keep your code readable for years to come.
Having spent more than 10 years writing PHP, I thought I knew everything I needed. I learned a lot more than I expected as an experienced developer, and can see a lot of benefit in this course for developers of any level.
“Clean code” covers a wide spectrum, but Freek and Christoph manage to boil the essence of beautiful programming down to a very useful set of guidelines. I especially recommend the section on static analysis for programmers looking to take their skills to the next level.
VAT will be calculated during checkout by Paddle.
We support Purchasing Power Parity.